[Node.js] 개발자 면접 질문 정리 ver.1

곽태민·2023년 4월 18일
0

이직하기

목록 보기
3/3

❗️본 글은 면접을 통한 대답을 잘 하지 못한 면접 질문 정리입니다.

JWT 구성


JSON Web Token의 약자로 JSON 객체이며, 서로간에 안전하게 정보를 전송할 수 있고, 작고 독립적인 방법을 정의하는 공개 표준( RFC 7591 )이다.

JWT 구조

간결한 형태의 JSON Web Token은 (.)으로 구분된 세 부분으로 구성이된다.

  • Header
  • Payload
  • Signature

xxxxx[Header].yyyyyy[Payload].zzzzz[Signature]

헤더는 일반적으로 JWT인 토큰 유형과 HMAC SHA256또는 RSA와 같이 사용 중인 서명 알고리즘의 두 부분으로 구성된다.

예를 들면, 아래와 같은 JSON은 Base64Url로 인코딩되어 JWT 첫 번째 부분을 구성한다.

{
	"alg": "HS226",
    "typ": "JWT"
}

Payload

토큰의 두 번째 부분은 클레임을 포함하는 Payloadek. 클레임은 entity(일반적으로 user) 및 추가 데이터에 대한 설명이다.

청구 유형에는 registered(등록), public(공개), private(비공개) 세 가지가 있다.

  • Registered Claims(등록 클레임)
    - 등록된 클레임들은 서비스에서 필요한 정보들이 아니라 토큰에 대한 정보들을 담기 위한 이름으로 정해진 클레임이다.
    • 등록된 클레임의 사용은 모두 optional이며, 이메 포함된 클레임 이름들은 다음과 같다.
      • iss(Issuer) : 토큰 발급자
        • exp(Expiration Time) : 토큰 만료 시간
        • sub(Subject) : 토큰 제목
        • aud(Audience) : 토큰 수신자/대상자
        • nbf(Not Before) : 토큰 활성 날짜
        • iat(Issued at) : 토큰이 발급된 시간, 토큰의 age가 얼마나 되었는지 판단 가능
        • jti : JWT 고유 식별자, 주로 중복 처리 방지를 위해 사용되며 일회용 초큰에 사용하면 유용
  • Public Claims(공개 클레임)
    - JWT를 사용하는 사용자가 원하는대로 정의할 수 있다.
    • 하지만 충돌 방지를 하려면 IANA JSON Web Token Registry에 정의하거나 충돌 방지 네임페이스를 포함하는 URI로 정의해야한다.
  • Private Claims(개인 클레임)
    - 이러한 클레임은 이용에 동의하고, Registered Claims도, Public Claims도 아닌 당사자들 간에 정보를 공유하기 위해 만들어진 Custom Claim이다.
    • Public Claims와는 달리 이름이 중복되어 충돌 가능성이 있다.

Payload의 예는 다음과 같고, Base64Url로 인코딩되어 JSON Web Token의 두번째 부분을 구성한다.

{
	"sub": "1234567890",
    "name": "Kim",
    "admin": true
}

서명된 토큰은 이 정보는 변조로부터 보호되지만 누구나 읽을 수 있다. 암호화 되지 않은 경우 JWT의 payload 또는 header 요소에 비밀 정보를 넣지 말자

Signature

서명 부분을 생성하려면 인코딩된 Header, 인코딩된 Payload, 암호, Header에 지정된 알고리즘을 가져와서 서명해야한다.

Header의 인코딩 값과 Data의 인코딩 값을 합친 후 주어진 Private Key로 해시하여 생성한다. 예를 들어 HMAC SHA256 알고리즘을 사용하려는 경우 서명은 다음과 같은 방식으로 생성된다.

HMACSHA256(
	base64UrlEncode(header) + "." +
    base64UrlEncode(payload),
    secret)

서명은 메시지가 도중에 변경되지 않았는지 확인하는데 사용되고, private key로 서명된 토큰의 경우 JWT의 송신자가 누구인지 확인할 수 있다.

Javascript 원시 타입과 참조 타입


Javascript는 원시 타입(Primitive Type)과 참조 타입(Reference Type)이라는 두 가지 타입의 자료형을 제공한다.

원시 타입 (Primitive Type)

원시 타입은 아래와 같이 총 여섯 가지가 있으며, 이 외의 모든 값은 참조(객체) 타입이다.

  • 숫자 (Number, Bigint)
  • Boolean
  • null
  • undefined
  • 문자열 (string)
  • Symbol

원시 타입 데이터는 변수에 할당될 때 메모리 상에 고정된 크기로 스택 영역에 저장되고 해당 변수는 원시 데이터 값을 보관한다.

또한, 원시 타입 자료형은 변수 선언, 초기화, 할당 시 값이 저장된 메모리 영역에 직접적으로 접근한다. 즉, 변수에 새 값이 할당될 때 변수에 할당된 메모리 블록에 저장된 값을 바로 변경한다.

원시 타입은 값 자체를 복사하기 때문에 원본 데이터 값이 바뀌더라도 기존 데이터 값을 유지한다.

let origin = 100;
let copy = origin;

console.log(copy); // 100

origin = 200;

console.log(copy); // 100

참조 타입 (Reference Type)

참조 타입에는 객체(Object), 배열(Array), 함수(Function)가 있다.

참조 타입 데이터는 크기가 정해져 있지 않고 변수에 할당될 떄 값이 직접 해당 변수에 저장되는 것이 아니라 변수의 값이 지정된 Heap 메모리의 주소 값을 저장한다.

따라서 참조 타입은 변수의 값이 저장된 메모리 블록의 주소를 가지고 있어서, 변수가 가지고 있는 메모리 주소를 이용해서 변수의 값에 접근한다. (Access By Refrence)

참조 타입은 주소 값을 참조하기 때문에, 원본 데이터의 값이 바뀌면 복사한 데이터 값도 변경된다.

let origin = {name: 'kim'}
let copy = origin;

console.log(copy.name); // kim

origin.name = "Lee";

console.log(copy.name); // Lee

// origin과 copy는 동일한 주소값을 참조하기 때문에 같은 객체를 나타낸다.

Null과 Undefined의 차이


null은 변수를 선언하고 빈값을 할당한 상태 (빈 객체), undefined는 변수를 선언하고 값을 할당하지 않은 상태다. 즉, undefined는 자료형이 없는 상태다.

따라서 typeof를 통해 자료형을 확인해보면 nullobject로, undefinedundefined가 출력되는 것을 확인할 수 있다.

typeof null // 'object'
typeof undefined // 'undefined'

null === undefined // false
null == undefined // true
null === null // true
null == null // true
!null // true

isNaN(1 + null) // false
isNaN(1 + undefined) // true

null

null은 원시값 (Primitive Type) 중 하나로, 어떤 값이 의도적으로 비어있음을 표현한다. undefined는 값이 지정되지 않은 경우를 의미하지만, null의 경우에는 해당 변수가 어떤 객체도 가리키고 있지 않다는 것을 이미한다.

nullundefined처럼 전역 객체 속성 중 하나가 아니라 리터럴 값이다.

Undefined

undefined는 원시값 (Primitive Type)으로, 선언한 후 값을 할당하지 않은 변수나 값이 주어지지 않은 인수에 자동 할당된다.

이 값은 전역 객체의 속성 중 하나로, 전역 스코프에서의 변수이기도 하다. 따라서 undefined 변수 초기 값은 undefined 원시 값이다.

undefined는 예약어가 아니여서 전역 스코프 외에서 변수 이름을 사용할 수 있다. 하지만 유지보수 디버깅에 어려움을 겪을 수 있으므로 피하는 것이 좋다.

아래의 경우 변수가 undefined를 반환한다.

  • 값을 할당하지 않은 변수
  • 메서드와 선언에서 변수가 할당하지 않은 경우
  • 함수가 값을 return하지 않을 경우

INNER JOIN과 OUTER JOIN


INNER JOIN

INNER JOIN은 데이터의 교집합으로 다음과 같은 방법으로 쿼리를 작성한다.

select * from A inner join B on A.번호 = b.번호;
select * from A,B where A.번호 = b.번호;

INNER JOIN된 결과를 보면 A테이블과 B테이블이 모두 가지고 있는 데이터만 검색됨.

OUTER JOIN

OUTER JOIN은 FULL OUTER JOIN을 빼고는 특정 테이블을 기분으로 데이터를 보여준다.

OUTER JOIN은 FULL OUTER JOIN / LEFT OUTER JOIN / RIGHT OUTER JOIN 크게 세 종류로 나뉜다.

먼저 LEFT OUTER JOIN은 왼쪽 테이블 기준으로 JOIN을 하는 것으로 왼쪽 테이블 A의 모든 데이터와 A테이블과 B테이블의 중복데이터들이 검색된다.

select * from A LEFT OUTER JOIN B ON (A.번호 = B.번호)
select * from A,B Where A.번호(+)=B.번호;

RIGTH OUTER JOIN은 오른쪽 테이블 기준으로 JOIN을 하는 것이다.

FULL OUTER JOIN은 왼쪽 테이블과 오른쪽 테이블의 합집합을 얻는다. 만약 A에 데이터가 있는데 B에 데이터가 없으면 B부분은 Null이 되고, 반대일 경우엔 A부분이 Null이 된다.

NestJS 의존성 주입


NestJS의 핵심 중 하나는 의존성 주입이다. (Defendency Injection)

의존성 주입 (Defendency Injection)

의존성 주입은 제어의 역전 (Inversion of controller)의 기술 중 하나다.

제어의 역전은 개발자가 제어해야할 영역을 프레임워크에게 믿고 맡기는 것이며, 의존성 주입은 개발자가 필요한 자원(클래스, 함수 ...)들을 외부에서 생성자를 통해서 넣어준다.

NestJS에서 의존성 주입하기

xxx.controller.ts

import { UserService } from './user.service';

export class UserController {
  constructor(private userService: UserService) {}
  
  @Get('')
  async findAll() {
  	return this.userService.findAll();
  }
}

xxx.service.ts

@Injectable()
export class UserService {
	async findAll() {
    	return this.userRepository.findAll();
    }
}

우선 의존성 주입의 대상을 지정해줘야 하는데 간단하다. 위의 service 코드처럼 @Injectable 데코레이터를 붙이면 의존성 주입의 대상이 된다.

이것은 Provider에 의한 주입대싱이 된다는 것으로 의존성을 주입한 클래스를 사용하기 위해서는 아래 코드와 같이 해당 컨트롤러가 주입된 모듈 내(xxx.moduel.ts), providers 필드에 배열로 전달해야한다.

xxx.module.ts

@Moudle({
  imports: [],
  controllers: [UserController],
  providers: [UserService],
})

export class UserModule {}

이제 의존성을 주입만 하면 된다. NestJS에서는 생성자(constructor)를 통하여 의존성 주입을 할 수 있다.

constructor(private userService: UserService) {}

의존성 주입을 했으니까 userService의 타입만 지정했을 뿐인데 userService 메소드를 호출할 수 있다. 이는 바로 userServiceuserService타입인 것을 NestJs Framework가 인지하고 알아서 치리해준다. (클래스 생성, 소멸, 주기 등...)

프로퍼티 스타일 주입

생성자를 통한 주입이 아니라, 클래스 필드의 형태로도 주입이 가능하며, 필드 위에 @Inject 데코레이터를 달아주면 된다.

@Inject()
private userService: UserService

하지만 공식문서에서는 생성자를 이용하여 의존성 주입을 권장한다.

profile
Node.js 백엔드 개발자입니다!

0개의 댓글