URI 란?

URI (Uniform Resource Identifier) 는 인터넷에 있는 자원을 나타내는 유일한 주소이다.

URI 에는 URL 과 URN 두 종류가 있는데 일반적으로 URL 을 많이 사용한다.


문법

scheme:[//[user[:password]@]host[:port]][/path][?query][#fragment]

RFC 3986 에 정의 되어 있는 URL 은 다음과 같은 형태를 나타낸다.

여러 구성 요소 중에 가장 중요한 세 가지는 scheme, host, path 이다.


요소

  1. scheme
    • 스킴이라고 하며 보통 프로토콜을 의미한다.
    • URL 나머지 부분들과 콜론(:) 으로 구분된다.
    • 대소문자를 구분하지 않는다.
    • 리소스에 어떻게 요청, 접근할 것인지 명시하는 how 를 담당한다.
    • ex) http, ftp, rtsp 등등..
  2. host
    • 접근하려고 하는 리소스를 가지고 있는 인터넷 상의 호스트 장비
    • 도메인명 (localhost) 또는 IP 주소 (127.0.0.1) 로 제공한다.
  3. port
    • 하나의 호스트에서 여러 개의 통신을 할 때 구분되는 값
    • 서버가 열어놓을 수 있다.
    • 포트 번호를 명시하지 않으면 default 로 80 이 사용된다.
  4. path
    • 리소스가 서버의 어디에 있는지 알려준다.


'공부 > Web' 카테고리의 다른 글

웹 접근성과 WAI-ARIA  (1) 2021.11.10
JWT (Json Web Token)  (3) 2021.05.22

Problem


Easy 문제입니다.

트리가 주어졌을 때 가장 아래쪽에 있고 가장 왼쪽에 있는 노드의 값을 구하는 문제입니다.



Solution

단순하게 풀면 됩니다.

최고 깊이 maxDepth 와 리턴할 결과값 result 를 인스턴스 변수로 선언합니다.

Preorder 로 트리를 순회하면 depth 가 1 높아졌을 때 마주하는 노드가 가장 왼쪽에 있는 노드입니다.

depth 가 1 높아지는 순간의 노드값을 저장해두면서 트리를 모두 순회하면 됩니다.

시간복잡도는 O(n) (n 은 노드의 갯수) 입니다.



Java Code

class Solution {
    int maxDepth = 0;
    int result = 0;
        
    public int findBottomLeftValue(TreeNode root) {
        goChild(root, 1);
        return result;
    }
    
    public void goChild(TreeNode node, int depth) {
        if (node == null) {
            return;
        }
        
        if (depth > maxDepth) {
            result = node.val;
            maxDepth = depth;
        }
        
        goChild(node.left, depth + 1);
        goChild(node.right, depth + 1);
    }
}


Problem


Easy 난이도의 문제입니다.

주어진 숫자 n 을 이진수로 바꾸었을 때 0 과 1 이 연속으로 나오는 구간이 있는지 없는지 판단하는 문제입니다.



Solution

n 을 이진수로 바꾸어 줍니다.

길이가 1 이라면 반복되는 구간이 없기 때문에 항상 true 입니다.

인덱스 0 에 해당하는 문자 even 을 구하고 이와 반대되는 문자 odd 를 구합니다.

이진수 String 을 순회하며 인덱스가 짝수일 때와 홀수일 때 각각 even 과 odd 와 일치하는 지 확인합니다.

시간복잡도는 O(n) 입니다.



Java Code

class Solution {
    public boolean hasAlternatingBits(int n) {
        String bit = Integer.toString(n, 2);
        
        if (bit.length() == 1) {
            return true;
        }
        
        char even = bit.charAt(0);
        char odd = even == '0' ? '1' : '0';
        
        for (int i = 0; i < bit.length(); i++) {
            if (i % 2 == 0 && even != bit.charAt(i)) {
                return false;
            }
            
            if (i % 2 != 0 && odd != bit.charAt(i)) {
                return false;
            }
        }
        
        return true;
    }
}


RSpec - Ruby Test Framework

RSpec 프레임워크는 BDD 프로세스에서 사용하는 툴입니다.

따라서 RSpec 테스트 코드에는 자세한 설명이 첨부되어 있습니다.

BDD (Behavior-Driven Development) 란 ?

TDD (Test-Driven Development) 의 유닛 테스트에서 더 나아가 테스트 케이스 자체가 요구사양이 되도록 개발하는 방식입니다. 단위 테스트 보다는 행위에 집중하여 테스트 메소드의 이름을 "이 클래스가 어떤 행위를 해야한다. (should do something)" 라는 식의 문장으로 작성하며 행위를 위한 테스트에 집중을 합니다.

  • Feature : 테스트에 대상의 기능/책임을 명시한다.
  • Scenario : 테스트 목적에 대한 상황을 설명한다.
  • Given : 시나리오 진행에 필요한 값을 설정한다.
  • When : 시나리오를 진행하는데 필요한 조건을 명시한다.
  • Then : 시나리오를 완료했을 때 보장해야 하는 결과를 명시한다.


RSpec 기본 구조

테스트의 기대되는 값과 실제 값을 비교하여 성공 여부를 리턴해줍니다.

expect(테스트 객체).to 비교 matcher(예상되는 )


describe

describe 키워드를 사용해서 어떤 함수를 설명하려는 지 명확하게 합니다.

예를 들어 클래스 함수를 참조할 때는 . 또는 :: 을 접두어로 하고 인스턴스 함수를 참조할 때는 # 을 접두어로 사용합니다.

describe '.authenticate' do
describe '#admin?' do


설명을 짧게 유지하기

명세의 설명이 40문자를 넘어가면 context 를 사용해서 쪼개야 합니다.

# Bad
it 'has 422 status code if an unexcepted params will be added' do

# Good
context 'when not valid' do
  it { is_expected.to respond_with 422 }
end


각 테스트는 한 가지만을 확인

테스트가 한 가지만을 확인해야 에러를 찾기도 쉽고 실패하는 테스트를 바로 찾을 수 있습니다.

같은 예제 안에서 여러 가지 예상이 나온다면 여러 행동으로 나누어야 합니다.

# 분리형
it { is_expected.to rsespond_with_content_type(:json) }
it { is_expected.to assign_to(:resource) }

# 통합형
it 'creates a resource' do
  expect(response).to respond_with_content_type(:json)
  expect(response).to assign_to(:resource)
end


가능한 모든 케이스를 테스트

유효한 경우, 유효하지 않은 경우를 모두 테스트 해야 합니다.

예를 들어 User 삭제 기능을 테스트 하려는데 before_action 으로 User 찾기 가 걸려있다면

before_action :find_user

def destory
  render 'show'
  @user.destory
end

보통은 삭제가 잘 되었는지만 확인하지만 유저를 찾지 못한 경우에 대해서도 테스트를 해주어야 합니다.

# Bad
it 'show user'

# Good
describe '#destory' do
  context 'when user is found' do
    it 'responds with 200'
    it 'shows the user'
  end

  context 'when user is not found' do
    it 'responds with 404'
  end
end


Expect vs Should

새 프로젝트에서는 항상 expect 문법만 사용합니다.

# Bad
it 'creates a resource' do
  response.should respond_with_content_type(:json)
end

context 'when not valid' do
  it { should respond_with 422 }
end

# Good
it 'creates a resource' do
  expect(response).to respond_with_content_type(:json)
end

context 'when not valid' do
  it { is_expected.to respond_with 422 }
end


필요한 데이터만 만들기

필요 이상으로 많은 데이터를 만들 필요는 없습니다.

최대한 적은 양의 데이터로 모든 케이스를 테스트 할 수 있어야 합니다.


Reference


#nil? #empty? #blank? #present?

Ruby 에는 존재 여부를 확인 할 때 위와 같은 네가지의 메소드를 사용합니다.

이 메소드들은 조금씩 차이점이 존재하는데 어떤 건지 알아봅시다.


#nil?

#nil? 은 Object 클래스의 메소드입니다.

Ruby 의 모든 클래스는 Object 메소드를 상속 받기 때문에 #nil? 은 어떤 클래스에서도 사용할 수 있습니다.

NilClass 값에 대해서만 true 를 리턴하고 이 외에는 전부 false 를 리턴합니다.

nil.nil?    # true

true.nil?   # false

5.nil?      # false

"".nil?     # false

[].nil?     # flase


#empty?

#empty? 는 string, array, hash, set 에서 사용되는 메소드입니다.

자료구조나 문자열의 길이가 0 일 때 true 를 리턴합니다.

nil 처럼 #empty? 메소드가 정의되어 있지 않은 오브젝트에서 사용하면 NoMethodError 가 발생합니다.

"".empty?           # true

" ".empty?          # false

"\t\n".empty?       # false

[].empty?           # true

{}.empty?           # true

Set.new.empty?      # true


#blank?

#blank? 는 Rails 메소드이며 모든 오브젝트에서 사용 가능합니다.

문자열에서 사용할 경우 빈 문자열 뿐만 아니라 공백까지 전부 true 로 리턴합니다.

"".blank?       # true

" ".blank?      # true

"\t\n".blank?   # true


array, hash, set 에서는 #empty? 와 동일하게 원소가 없는 경우에 true 를 리턴합니다.

[].blank?       # true

{}.blank?       # true

Set.new.blank?  # true

[nil].blank?    # false

["", ""].blank? # false


true, false, nil 값에 대해서 각각 다음과 같이 리턴합니다.

true.blank?     # false

false.blank?    # true

nil.blank?      # true


#present?

#present? 는 #blank? 와 완전히 반대되며, 마찬가지로 Rails 메소드입니다.


Table


Diagram


Reference


Ruby Regular Expressions

루비 정규 표현식은 문자열 내의 특정 패턴을 찾는 데 도움을 줍니다.

루비에서 정규식을 사용할 때는 양 끝에 슬래쉬(/) 를 사용합니다.


Example

1. =~

# 'world' 단어 찾기
"Hello world!" =~ /world/   # 6
"Hello world!" =~ /worla/   # nil

=~ 식으로 비교하면 정규 표현식이 시작되는 인덱스를 리턴합니다.

해당되는 인덱스가 존재하지 않으면 nil 을 리턴합니다.


2. match

if "Hello world!".match(/world/)    # <MatchData "world">
    puts "world is found"
end

match(/regex/) 메소드를 통해 정규식 포함 여부를 알 수 있습니다.

만약 정규 표현식에 해당되는 값이 없다면 nil 을 리턴합니다.

간단히 true / false 여부만 알고싶다면 include? 메소드를 사용할 수 있습니다.

Problem


Medium 난이도의 문제입니다.

n 이하의 수들이 분모가 되고 각 수가 1 이하가 되도록 하는 수들의 리스트를 구하는 문제입니다.



Solution

단순하게 2중 for문으로 구하면 됩니다.

중복 확인은 Set 을 사용합니다.



Java Code

class Solution {
    public List<String> simplifiedFractions(int n) {
        List<String> result = new ArrayList<>();
        Set<Float> set = new HashSet<>();
        
        for (float i = 2; i <= n; i++) {
            for (float j = 1; j < i; j++) {
                float div = j/i;
                
                if (!set.contains(div)) {
                    int child = (int) j;
                    int parent = (int) i;
                    
                    result.add(child + "/" + parent);
                    set.add(div);
                }
            }
        }
        
        return result;
    }
}


Problem


Medium 난이도의 문제입니다.

k 를 만들 수 있는 피보나치의 합들을 찾고 그 중에서 갯수가 가장 작은 값을 구하는 문제입니다.



Solution

Greedy로 문제를 풀 수 있습니다.

k 보다 작은 피보나치 수열을 전부 TreeSet 에 담고 k 보다 작으면서 가장 큰 피보나치 수 를 계속해서 빼면 됩니다.

따로 증명은 안해봤지만 피보나치 수 중에 1, 1, 2, 3 이 있기 때문에

가장 큰 수부터 계속 빼면 어떻게든 0 으로 나누어 떨어질 것으로 생각되었습니다.

TreeSet.floor(n) 은 Set 에 n 이 존재하면 n 을 리턴, 아니라면 n 보다 작으면서 가장 가까운 수를 리턴합니다.



Java Code

class Solution {
    public int findMinFibonacciNumbers(int k) {
        if (k < 2) {
            return 1;
        }
        
        TreeSet<Integer> set = new TreeSet<>();
        
        for (int lo = 0, hi = 1; hi < k;) {
            int sum = lo + hi;
            set.add(sum);
            lo = hi;
            hi = sum;
        }
        
        int count = 0;
        
        while (k > 0) {
            int num = set.floor(k);
            k -= num;
            count++;
        }
        
        return count;
    }
}


+ Recent posts