CS 기본
1. 프로세스란
운영체제에서 자원을 할당 받아 독립적 메모리 공간을 갖는 애플리케이션의 인스턴스
2. 스레드란
프로세스 내에서 작업 처리의 단위
프로세스가 할당 받은 자원을 이용해서 작업의 효율적인 병렬 실행을 가능
스레드마다 하나의 고유 스택(Stack)을 갖고 있음
3. 멀티 스레드 환경에서 고려해야 하는 점
다수의 스레드가 공유 데이터에 동시에 접근하는 경우 데이터 손상을 막기 휘해 동기화 처리를 고려
동기화는 락을 획득하고 해제하는 과정에서 성능을 해치기에 어떤 동기화 처리를 해야 할지 고려
4. 동기화의 종류
동기화는 크게 닷넷 환경 기준에서는 ‘유저 모드 동기화’와 ‘커널 모드 동기화’ 그리고 이 둘을 합친 ‘복합 스레드 동기화’가 있음
[유저 모드 동기화]
Monitor 클래스 (lock 키워드)
volatile 키워드
Interlocked 클래스
[커널 모드 동기화]
Mutex 클래스
Semaphore 클래스
AutoResetEvent 클래스
ManualResetEvent 클래스 등
5. 닷넷 기준 스레드 안정성
닷넷프레임워크, 닷넷 코어 이상의 FCL(Framework Class Libaray)은 모든 정적 메서드에 대해서 CLR 차원에서 스레드-안정적임을 보장함
public static Int32 Max(Int32 val1, Int32 val2)
{
return (val1 < val2) ? val2 : val1;
}
위 Max() 메서드 파라메터 두 개의 Int32는 값 타입이므로 복사본이 전달되기 때문에 여러 스레드가 동시에 호출해도 각 스레드에서 별개로 고유의 데이터를 가지고 작업을 수행함
6. 닷넷 / JS 가비지콜렉터 작동 순서
[닷넷]
CLR이 초기화 될때 0세대, 1세대, 2세대의 크기가 미리 결정
GC가 실행되는 시점은 CLR이 힙 영역 메모리 상태 판단으로 언제 실행되는지는 알 수 없음
GC 수행 - GC 수행 스레드 제외, 모든 스레드 중단
적재가 시작되었던 메모리 부터 순차적으로 객체를 순회하면서 그래프 작성
순회중 다른 객체를 참조하고 있으면 참조하고 있는 객체 역시 그래프 추가
모든 객체를 재귀적으로 순회하며 트리 구조의 그래프 생성
순회가 끝나면 그래프에 해당되지 않은 객체는 소멸
소멸 되지 않은 객체는 세대 증가
메모리에서 객체 재배열 (단편화 제거)
[V8 엔진]
New space(새로운 생성된 객체)와 Old space(gc로 소멸되지 않고 살아 남은 객체) 영역으로 나뉨
그외, Pointer space 영역에 다른 객체들을 참조하는 객체가 저장되고, Data space에는 문자열, 실수 등 데이터 객체들이 저장
New space 영역에는 두개의 Semi space로 나뉘는데 첫 객체 생성시 첫번째 Semi space에 할당이 되고, GC 이후 생존된 객체는 두번째 Semi space에 할당이 됨
그리고 New space 영역에서 최종적으로 생존된 객체는 Old space 영역으로 이동
기본적으로 닷넷 GC와 같은 Mark-And-Sweep 매커니즘으로 모든 객체를 순회하여 트리 구조 그래프 생성
GC 수행시 위 트리 구조 그래프 기준으로 그래프에 해당 되지 않은 객체 (더 이상 루트 참조가 없는 객체) 는 소멸하고, 소멸되지 않은 객체는 위 New space / Old space 조건에 의해 이동
메모리에서 객체 재배열 (단편화 제거)
7. Block VS Non-Block / Sync VS Async
Block와 Non-Block의 차이점은 ‘제어권’ 을 쥐고 있느냐, 넘겨주느냐의 차이
Block은 제어권을 계속해서 쥐고 있어 작업을 독점
Non-Block은 작업의 상태(완료 되었는지, 오류가 발생 되었는지)와 같이 제어권을 넘겨주고 다른 작업을 처리 할 수 있도록 협조
Sync와 Async는 시간 시점의 차이
Sync는 작업이 실행될때 해당 작업이 완료 될때 까지 기다리고 다음 작업을 처리
Async는 작업 시작을 호출 하고 해당 작업이 끝날때 까지 기다리지 않고 다른 작업을 처리 [다른 작업을 처리 하는 대상은 멀티 스레드 환경에서 또 다른 워커 스레드 일 수 있음]
7. CSRF (Cross-Site Request Forgery, 크로스 사이트 요청 위조) 란?
CSRF 동작 원리
사용자 로그인: 사용자가 신뢰할 수 있는 사이트(A)에 로그인하여 세션 쿠키를 얻음.
악의적인 요청 준비: 공격자는 악성 사이트(B)를 준비, 이 사이트는 사이트(A)에 특정 요청을 보낼 수 있는 링크나 스크립트를 포함.
사용자 유도: 사용자가 악성 사이트(B)를 방문하면, 해당 사이트에 포함된 스크립트나 링크가 자동으로 사이트(A)에 요청을 전송.
요청 실행: 사이트(A)는 사용자가 이미 로그인되어 있는 상태이므로, 악성 요청이 유효한 사용자로부터 온 것처럼 처리하여 해당 요청을 실행 (계좌 이체나 비밀번호 변경 같은 중요한 작업)
아키텍처 기본
1. 의존성 주입이란
객체간 결합도를 낮추고 유연성을 확보하기 위한 방법
관심사의 분리로 한 객체가 어떤 객체에 의존할 것인지는 별도의 관심사로 보고 분리 하는 방법
2. 의존성 주입을 처리 했을때 이점
코드의 재사용성 증가
객체간 결합도 감소 및 객체별 단위 테스트 용이
3. 의존성 주입시 사용되는 객체로 불변 객체를 사용하면 좋은점
불변 객체는 한번 생성되면 상태가 변하지 않기에 스레드에 안전하고 동기화가 필요 없어 성능면에서 유리
또한 외부로 부터 예기치 않은 값 변경을 방지하여 오류를 감소시킬 수 있음
4. 클린아키텍처 관련
클린 아키텍처는 각 계층의 경계를 중요시 하게 여기고 의존성 규칙을 따르는 것이 핵심
정책의 기준 영역 / 해당 정책에 따라 역할을 수행하는 영역 등 분리
모듈의 의존성은 반드시 단방향으로 이어져야 함 [바깥쪽에서 안쪽으로, 안쪽에선 바깥쪽 구조를 몰라야 된다.]
5. AOP(Aspect Oriented Programming, 관점 지향 프로그래밍) 를 도입할때 많이 쓰이는 디자인 패턴
Aspect Oriented Programming 의 약자로 관점 지향 프로그래밍
코드상에서 자주 반복되는 코드들이 흩어져 있는 경우 공통의 관심사로 보고, 이를 공통된 모듈로 처리 하는 방식
기본적으로 메서드 인터셉터를 통해 특정 메서드 호출 전 또는 이후에 공통으로 처리 되는 로직을 수행 하도록 한다.
웹 서버 환경에서는 미들웨어나 필터를 통해 인증/인가 역할 또는 전역 예외 처리 등을 한곳에서 집중해서 처리 하도록 한다.
6. API Gateway 설명
MSA 아키텍처 구조 환경에서 서비스끼리 직접 API 요청을 하지 않고 API Gateway를 통해서 서비스 요청이 되도록 처리하는 것이다.
인증 관련하여 인증 토큰을 발급하는 역할
API 라우팅 역할도 한다. 외부로 노출 하지 않고 내부망으로 구성 되있는 서버에 요청을 하여 보안적으로 좋고 서버의 로드 밸런싱 처리로 서버 부하를 분산 할 수 있다.
7. MSA 설계시 고려해야 할점
분리할 핵심 기능의 도메인을 명확하게 정의해야함 서비스별로 DB가 분리 될때 데이터간 의존성 체크 서비스가 분리 되어 있어 통합 테스트가 원할히 되지 않기에 이에 대해서 고민이 필요
8. CQRS 설명
CQRS(Command Query Responsibility Segregation) 패턴은 대량의 트래픽이 발생 될때 명령 트래픽과 조회 트래픽을 분리 처리 할 수 있는 방법중 하나
데이터 생성 도메인과 데이터 조회 도메인을 나눔으로 도메인간 의존성을 분리 시켜 복잡한 비즈니스 로직을 겉어 내는 이점을 동시에 가질 수 있음
보통 데이터 생성 데이터 베이스와, 데이터 조회 데이터 베이스를 분리해서 처리하고, 조회용 데이터 베이스는 빠른 데이터 조회를 목적으로 비정규화된 형태로 데이터를 보관함 그래서 RDBMS보단 NoSQL종류의 데이터 베이스를 사용한다.
9. CQRS 설계시 Command 도메인에 해당되는 DB와 Read 도메인에 해당되는 DB가 분리 되어 있을때 데이터 동기화 방법 [이벤트 소싱 처리 및 이벤트 브로커를 통해 동기화 방법]
CQRS 에서 데이터 생성 DB / 데이터 조회 DB가 분리 되어 있는 경우 서로간 데이터 동기화를 고려해야 한다.
이때 대부분의 해소 방법은 이벤트 소싱 패턴을 사용한다.
이벤트 소싱 패턴은 데이터가 생성 되면 데이터 베이스에서 직접 상태를 변경하거나 생성하는 것이 아닌 이벤트 단위로 이벤트를 생성 한다.이때 이벤트를 생성해서 보관하는 장소를 이벤트 스토어라고 하는데, 이벤트 스토어에는 UPDATE / DELETE 는 존재 하지 않고 오직 INSERT만 존재 한다.
이벤트 스토어에서 데이터 조회는 이벤트 스트림 방식을 통해 구독하여 데이터 조회 DB에 동기화 한다.
이때 이벤트 스트림 처리는 MQTT나 Kafka 등 이벤트 브로커를 통해 처리할 수 있다.
Node.js (JavaScript / TypeScript)
1. JavaScript 제너레이트
반복 가능한 이터러블(iterable) 객체를 만듬
외부에서 next()를 호출하면 가장 가까운 yield<value> 문을 만날 때까지 실행이 지속됨
function* generateSequence() {
yield 1;
yield 2;
return 3;
}
let generator = generateSequence();
let one = generator.next();
alert(JSON.stringify(one)); // {value: 1, done: false}
let two = generator.next();
alert(JSON.stringify(two)); // {value: 2, done: false}
let three = generator.next();
alert(JSON.stringify(three)); // {value: 3, done: true}
‘done’ 속성은 해당 이터러블이 종료 되었는지 여부
2. JavaScript가 싱글스레드라서 일어나는 일
setTimeout(() => console.log('test'), 0); // test 문자 출력 되지 않음
while (true);
JavaScript의 비동기 처리 매커니즘에 의해 setTimeout() 메서드를 만나면 브라우저 또는 JS엔진에 의해 타이머 관련 API를 호출하고 결과를 Task Queue로 등록
이후 Call Stack이 비어 있는 경우 Task Queue에서 API 결과를 꺼내 Call Stack에 등록 시켜 주는데 무한루프 while로 인해 Call Stack이 비어 있지 않은 상태로 결과를 받아 출력 할 수 없다.
3. isNan() / Number.isNan() 차이
isNan() : 인자로 들어온 값을 숫자로 캐스팅 해서 숫자인지 판별
Number.isNan() : 인자로 들어온 값 자체가 NaN인지 아닌지 판별
isNan() 메서드는 임의로 숫자로 캐스팅 해서 판별 하기에 의도치 않은 결과를 받을 수 있어 Number.isNan() 메서드를 사용하는 것이 좋다.
4. 생성자 함수 설명
new 키워드로 함수를 호출해서 사용할 수 있음
생성자 함수는 암시로 this에 빈 객체를 할당하여 정의한 속성을 추가해서 해당 this를 반환 하게 됨
function User(name) {
// this = {}; (빈 객체가 암시적으로 만들어짐)
// 새로운 프로퍼티를 this에 추가함
this.name = name;
this.isAdmin = false;
// return this; (this가 암시적으로 반환됨)
}
6. Node.js 는 단일 스레드 환경인데 비동기 처리를 할때 고려해야 하는 점
JavaScript에서 비동기를 처리하는 매커니즘을 볼때 비동기 관련 코드(setTimeout, Promise, ajax 등)를 만나면 현재 실행되고 있는 js환경 (브라우저 또는 V8 엔진 등)의 API로 요청이 되고 Task Queue 또는 Micro Queue로 결과를 받게 된다.
[Micro Queue 먼저 체크 하고 이후 Task Queue를 체크 한다.] [Promise 비동기 처리의 결과는 Micro Queue 에 등록]
이후 Queue에 등록된 결과는 Call Stack이 비어 있는 경우 Call Stack에 결과를 등록하게 되는데 동기 코드중 오래 걸리는 처리가 있는 경우 결과를 받을 수 없어 지연 되는점을 고려해야 한다.
7. Node.js 에서 멀티 스레드 처리 하는 방법
worker_thread 모듈을 이용해서 별도의 스레드 풀을 이용해 멀티 스레드로 처리 할 수 있음
8. Nest.js 에서 의존성 주입에 대해 내부적으로 동작되는 방식 설명
참고 자료 - Nest.js는 실제로 어떻게 의존성을 주입해줄까?
@Injectable 어노테이션 관련 코드를 보면 NestFactory.create() 메서드에서 NestContainer 를 생성하고 의존성으로 처리 되는 객체가 NestContainer에 등록
그리고 모든 의존성을 스캔하고 스캔한 의존성들을 인스턴스화 해서 주입 시켜 준다.
9. Nest.js 라이프사이클 훅 설명
[1. 초기화 훅]
onModuleInit() : 모듈이 초기화 되고 종속성이 해결되면 호출
import { Injectable, OnModuleInit } from '@nestjs/common';
@Injectable()
export class ExampleService implements OnModuleInit {
onModuleInit() {
console.log(`모듈이 초기화될 때 실행`);
}
}
OnApplicationBootstrap() : 모든 모듈이 초기화된 뒤, Nest.js 애플리케이션이 실행되기 전의 초기화 단계
import { Injectable, OnApplicationBootstrap } from '@nestjs/common';
@Injectable()
export class ExampleService implements OnApplicationBootstrap {
onApplicationBootstrap() {
console.log('부트스트랩 단계...');
this.bootstrap();
}
private bootstrap() {
// 애플리케이션 Bootstrap 단계에서 수행할 초기화 작업을 작성하는 코드
}
}
[2. 종료 훅]
OnModuleDestroy() : 모듈이 소멸되기 전에 실행되어, 모듈이 종료되는 시점에 무언가 수행하고 싶을 때 사용할 수 있음
import { Module, OnModuleDestroy } from '@nestjs/common';
@Module({})
export class ExampleModule implements OnModuleDestroy {
onModuleDestroy() {
console.log('모듈이 종료되기 전에 실행된다');
}
}
BeforeApplicationShutdown() : onMouduleDestory() 이후 애플리케이션이 완전히 종료되기 직전에 호출된다.
OnApplicationShutdown() : OS의 종료 시그널을 받아 처리할 수 있다. 컨테이너의 수명 주기를 관리하기 위해 쿠버네티스와 같은 서비스에서 사용된다.
import { Injectable, OnApplicationShutdown } from '@nestjs/common';
@Injectable()
export class ExampleService implements OnApplicationShutdown {
onApplicationShutdown(signal?: string) {
console.log('애플리케이션 종료 전 실행된다');
}
}
10. Nest.js 요청 수명 주기
미들웨어 : 전역으로 바인딩된 미들웨어 부터 실행, 이후 모듈에 바인딩 된 미들웨어 실행
가드 : 주로 인증 처리를 할 때 사용한다.
전역 가드, 컨트롤러 가드, 라우트(Route) 가드 순서대로 실행
app.useGlobalGuard() 모듈을 통해 구성요소를 사용하거나 제공하는 경우 전역적으로 바인딩된다.
가드 데코레이터가 컨트롤러 클래스 앞에 있으면 컨트롤러에 바인딩되고, 내부에 선언되면 라우터에 바인딩된다.pre-interceptors (인터셉터) : 주로 post-interceptor를 위한 변수 선언, 함수 실행으로 사용
파이프, 컨트롤러 또는 서비스에서 발생하는 모든 오류는 인터셉터의 catchError에서 읽을 수 있다.
인터셉터가 RxJS Observables를 반환하면 옵저버블은 선입선출 방식으로 해결한다.파이프 : 요청된 바디를 원하는 형식으로 변환 및 유효성 검사 처리
컨트롤러 : 라우터 역할을 수행
서비스 : 해당 요청에 대한 비즈니스 로직 수행
post-interceptors (인터셉터) : 주로 pre-interceptor 로직을 가지고 응답한 데이터를 가공하거나 전체 로직의 속도 측정
최종적으로 성공적인 응답 데이터를 보냄예외 필터 : 예외 처리를 담당. 에러 메세지를 원하는 형태로 가공해서 응답
11. Nest.js 메모리 누수 분석 방법
Node.js 실행 시 chrome의 inspect툴을 사용해서 분석
- Heap snapshot : 현재 페이지의 힙 상태를 기록하고 분석, 성능 개선 전후로 스냅샷을 비교할 때 유용
Allocation instrumentation on timeline : 메모리 누수가 의심되는 시나리오를 수행, 기록을 진행하는 동안 타임라인에 메모리 할당과 해제가 표시 되는데 이를 분석
(객체에 메모리가 할당되면 ‘파란색 막대’, 가비지 컬렉션으로 메모리 해제시 ‘회색 막대’로 표시)- Allocation sampling : Allocation instrumentation on timeline과 비슷한 방식으로 기록하지만, Allocation sampling은 메모리 할당을 함수 단위로 간단하게 기록
[TypeScript인 경우]
node --loader ts-node/esm --inspect src/index.ts
[JavaScript 경우]
node --inspect src/index.js
12. Nest.js 메모리 누수 원인이 될만한 상황
잘못된 클로저 사용
잘못된 전역 변수 관리
13. TypeScript 에서 타입 시스템 설명 [구조적 서브타이핑 / 명시적 서브타이핑]
[TypeScript는 구조적 서브타이핑 시스템]
TypeScript는 구조적 서브타이핑 시스템으로 상속 관계가 명시 되있지 않아도 객체의 프로퍼티 기반으로 일치하면 같은 타입으로 취급
한편 자바나 C#같은 언어는 명시적 서브타이핑으로 객체가 상속 관계로 정확히 일치해야 같은 타입으로 취급함
type Food = {
/** 각 영양소에 대한 gram 중량값 */
protein: number;
carbohydrates: number;
fat: number;
}
const burger = {
protein: 29,
carbohydrates: 48,
fat: 13,
burgerBrand: '버거킹'
}
‘burger’객체도 ‘Food’ 타입과 같은 타입으로 취급
14. 구조적 서브파이핑 에서 함수 파라메터로 객체를 직접 생성해서 인자로 전달하는 경우 타입 호환이 되지 않는 이유 [객체의 ‘fresh’ 상태]
함수 파라메터로 객체를 직접 생성해서 인자로 전달하는 경우 객체가 ‘fresh’ 하다고 판단하여 타입 호환에 이점보단 부작용이 더 크기에 이를 방지하기 위해 오류로 발생됨
function calculateCalorie(food: Food){
return food.protein * 4
+ food.carbohydrates * 4
+ food.fat * 9
}
const calorie = calculateCalorie({
protein: 29,
carbohydrates: 48,
fat: 13,
burgerBrand: '버거킹'
})
/** 타임검사결과 : 오류 (NOT OK)*/
15. TypeScript - unknown vs any
unknown type은 typescript 3.0 에서 새로 나온 타입
unknown type은 any type과 동일하게 어떤 타입이던 할당은 가능 하지만, 반대로 다른 타입에 할당은 불가능 함
let varr:unknown;
let booleanType:boolean = varr; // boolean 타입에 unknown 타입 할당 불가능
반면 any type은 never타입 이외에 모든 타입에 할당이 가능하기에 불안전 함
const anytype:any = true;
const hi:string = anytype; // string 타입에 any 타입 할당이 가능
hi.toUpperCase(); // 런타임 에러
16. TypeScript - Object vs any
Object 타입은 모든 ‘객체’를 허용하는 타입으로 오직 객체만 할당할 수 있고, 원시 타입은 할당이 불가능
any 타입은 타입이 없는 javascirpt 에서 처럼 모든 타입을 허용하는 타입
17. TypeScript - Interface vs Type
Interface 및 Type 둘다 타입을 정의하는 방법
Interface는 선언 병합이 됨. 즉, 같은 Interface 이름으로 여러개를 정의 하면 병합됨
interface Person {
name: string;
}
interface Person {
speak(): void;
}
// 기존 인터페이스 Person에 speak() 함수가 선언 병합됨
const person: Person = {
name: 'Mike',
speak: () => {},
}
Interface는 Type과 다르게 객체에만 사용 가능
Type은 Interface 에서 구현 가능한 기능을 모두 구현할 수 있지만 Interface는 Type의 유니온 조건 등 처리는 불가능 하다.
따라서 평상시에는 Interface를 사용하고 Interface로 처리가 불가능한 특정 조건이 필요한 상황은 Type을 사용한다.
18. TypeScript가 컴파일되는 과정 대략적 설명
TypeScript 소스코드를 AST(Abstract Syntax Tree, 추상 문법 트리)로 변환
이후 타입 검사 및 타입 바인딩 수행
타입 바인딩이 완료 되면 JavaScript 소스코드로 변환
19. 클로저(Closure)
반환된 내부 함수가 외부에서 호출 되었을때도 그 반환된 함수의 스코프에 접근 가능 할 수 있는 메서드를 말함.
DataBase
1. 인덱스의 자료구조
B+Tree
2. 인덱스는 왜 B+Tree 자료구조를 사용하는지
기본적인 Tree구조 기반으로 빠른 데이터 검색이 가능하고, 동시에 B+Tree 자료구조는 하위 노드끼리 연결이 되어 있기에 범위 검색시 상위 노드 재 탐색 없이 바로 다른 부모의 하위 노드로 접근 하여 탐색 가능한 이점
3. 클러스터 인덱스 / 넌클러스터 인덱스 차이
클러스터 인덱스 : PK컬럼이 자체 인덱스로 사용
자동 정렬이기에 데이터가 추가, 수정 될때마다 정렬이 일어나 INSERT / UPDATE / DELETE 쿼리가 비교적 비용이 큼넌클러스터 인덱스 : 별도의 인덱스 페이지 테이블이 존재하여 매번 정렬 작업이 불필요
INSERT / UPDATE / DELETE 비용이 적은 대신 SELECT 쿼리시 인덱스 페이지를 참조해야 하기 때문에 비교적 비용이 큼
4. MSSQL 과 Postgress 를 실무적으로 경험했을때 차이점 [관계형 데이터 베이스(RDBMS) 와 객체-관계형 데이터 베이스 차이점(ORDBMS)]
[MSSQL]
PK컬럼으로 클러스터 인덱스 지원 [데이터 자동 정렬]
[Postgress]
PK컬럼이 ‘Index’를 자동 생성하지만 데이터 정렬을 보장하지 않는다.
5. Partitioning (파티셔닝) 과 Sharding (샤딩) 설명
[Partitioning (파티셔닝)]
컬럼 단위로 테이블을 분리하여 관리
[Sharding (샤딩)]
Row 단위로 테이블을 분리하여 관리
6. Partitioning (파티셔닝) 은 어떤 상황에서 하는지 / Sharding (샤딩) 은 어떤 상황에서 하는지
[Partitioning (파티셔닝)]
테이블의 컬럼이 지속적으로 늘어다서 사용되지 않은 컬럼이 많은 경우 컬럼 단위로 나누어 관리해서 데이터 조회 성능을 올린다.
[Sharding (샤딩)]
물리적으로 Database를 분할해서 데이터 관리에 용이
축척된 데이터에서 인덱스 구조의 깊이(B+Tree)가 깊어지면 효율적인 검색이 불가능하기에 수평적으로 데이터를 분할해서 성능을 향상 한다.
7. SQL 인젝션 방어 방법
단일 쿼리 사용시 preparestatement 사용
ORM 라이브러리나 프레임워크 도입
8. ORM 도입시 N + 1 문제에 대한 설명
관계 설정된 엔티티를 조회하는 경우 조회된 데이터 갯수 만큼 조회 쿼리가 추가로 발생해서 데이터를 읽어 오는 문제 [불필요한 성능 저하]
이런 현상은 Lazy Loading사용으로 엔티티를 검색 했을때 필요한 순간 데이터를 가져오는 방식으로 인해 생긴 현상이다.TypeORM 에서는 이런 N + 1 문제를 find() 메서드에 relations 옵션을 적용하거나, QueryBuilder를 사용해서 해결
닷넷 환경 EntityFramework 에서는 참조되는 엔티티를 Include() 메서드 또는 ThenInclude() 메서드 사용으로 Eager Loading, 즉시 로딩을 사용하면 단일 쿼리를 생성해서 해결 할 수 있습니다.
9. 쿼리 조회시 인덱스가 무시 되는 상황 [조건 대상 컬럼이 가공 되거나, LIKE 조건을 사용해서 풀 스캔 처리 되는 경우 등]
WHERE 조건에 LIKE %% 검색이 되는 경우
검색 대상 컬럼이 가공 되는 경우 (강제 캐스팅 처리 등)멀티 컬럼 인덱스 설정된 경우 (a, b, c로 구성된 인덱스) 선두 인덱스가 사용 되지 않은 경우 (일부 b, c만 검색하는 경우)
다중 열에 인덱스가 설정되어 있을 때, 선두 열이 반드시 사용되어야 인덱스를 탈 수 있다. (선두 열 법칙)
예를 들어, (a, b, c)로 구성된 인덱스에서:
a, b 또는 a, b, c로 검색하면 인덱스를 사용.
b, c로 검색하면 인덱스를 타지 않음. a가 포함되지 않으면 인덱스를 사용할 수 없기 때문.
10. DB에서 데드락 발생 원인
서로 다른 트랜잭션이 서로의 작업을 무한정 기다리는 상태를 데드락이라고 말하며,
트랜젝션1이 A테이블 작업, 트랜잭션2가 B테이블을 작업하고 커밋전에
트랜잭션1이 B테이블 INSERT or DELETE or UPDATE 행위
트랜잭션2가 A테이블 INSERT or DELETE or UPDATE 행위 시 서로 트랜잭션 commit을 기다리기 때문에 데드락이 발생된다.해결방안은 SET LOCK_TIMEOUT문을 통해 일정 시간이 지나면 쿼리를 취소한다. (예방기법)
트랜잭션 A가 트랜잭션 B에 의해 잠금된 데이터를 요청할 때 트랜잭션 A가 먼저 들어온 트랜잭션이라면 대기 시키고,
트랜잭션 A가 나중에 들어온 트랜잭션이라면 포기시킨다. (Wait-Die 방식)
11. DB 트랜잭션 isolation level 이란?
데이터베이스 트랜잭션이 서로 간섭하지 않도록 격리되는 정도를 설정하는 방식
1. Read Uncommitted (읽기 미완료)
설명: 트랜잭션이 커밋되지 않은 데이터를 다른 트랜잭션이 읽을 수 있음.
문제점: Dirty Read가 발생할 수 있음.
성능: 가장 낮은 격리 수준으로 성능이 좋지만, 데이터 일관성이 보장되지 않음.2. Read Committed (읽기 커밋 완료)
설명: 트랜잭션은 커밋된 데이터만 읽을 수 있음.
문제점: Non-repeatable Read가 발생할 수 있음.
이는 동일한 트랜잭션 내에서 같은 데이터를 여러 번 읽을 때, 중간에 다른 트랜잭션이 해당 데이터를 수정할 수 있어 읽을 때마다 다른 값을 읽는 문제.
성능: 적절한 성능과 일관성을 제공.3. Repeatable Read (반복 가능 읽기)
설명: 트랜잭션 동안 읽은 데이터는 다른 트랜잭션에서 수정할 수 없습음.
문제점: Phantom Read가 발생할 수 있음.
이는 트랜잭션 내에서 같은 쿼리를 여러 번 실행할 때, 중간에 다른 트랜잭션이 새로운 행을 삽입하면 결과에 새로 삽입된 행이 포함되는 문제.
성능: Read Committed보다 격리 수준이 높아 성능이 약간 떨어질 수 있지만, 일관성이 강화.
12. 트랜잭션 A, B가 있을 때 Dirty write 처리 방법
ULID (Universally Unique Lexicographically Sortable Identifier) 컬럼이나 ROW VERSION 컬럼을 추가 하여 버전 관리 값으로 사용해서 데이터가 조작 될때
조회 시점의 ROW VERSION과 일치 하는지 체크하여 불일치 할 경우 업데이트를 중단시킬 수 있음.
13. 멀티 컬럼 인덱스 사용시 주의 사항
멀티 컬럼에 인덱스를 설정할 때, 쿼리가 인덱스의 선두 열을 기준으로 조회되어야만 인덱스를 효과적으로 사용할 수 있다.
예를 들어 (a, b, c)에 대한 인덱스가 있다면, a가 먼저 검색 조건에 포함되어야 인덱스를 탈 수 있다.
기타
1. 최근 진행 했던 프로젝트 또는 업무 중 힘들었던 경험과 해결해 나간 경험
2. 코드 리뷰 경험이 있는지
3. DDD(Domain-Driven-Design, 도메인 주도 개발) 개발 방법론을 도입할때 이점과 고려해야 할 사항
각각의 기능적인 문제 영역을 정의하고, 그 문제 영역에서 사용되는 도메인과 그 도메인을 사용하는 비즈니스 중심으로 설계하는 방식
[DDD(Domain-Driven-Design] 특징
- 데이터 중심이 아닌 도메인의 모델과 로직에 집중
- 기획, 개발 등 모든 팀에서 업무 용어를 통일 해서 단일화된 커뮤니케이션 방식 사용
- 도메인 모델과 코드가 함께 움직임
기존 시스템이 레거시 기술로 만들어진 경우 한번에 수정하는 빅뱅 방식 보다는 필요한 기능을 구별하고, 그것을 점진적으로 개선하는 방법 으로 잘 맞는 개발 방법론중 하나
이런 방식은 기존 레거시와, 신규 기술을 동시에 운영 [MSA 설계방식] 하면서 한정적인 시간, 인력을 유연하게 리소스를 사용하면서 점차적으로 시스템을 개선하는 방식에서 유용함단점으로는 클린아키텍처 설계 또는 그외 기타 다른 아키텍처 설계로 인해 많은 코드 발생
그리고 각 도메인에 대한 높은 이해도 필수장점으로는 새로운 기능 및 요구사항에 대해 유연성이 높아짐
그리고 비즈니스 로직에 집중하여 진행 가능
4. 도커 [이미지란, 컨테이너란]
컨테이너 : 호스트 OS를 공유하며, 격리된 환경에서 어플리케이션 구동 매커니즘을 제공해주는 가상화된 런타임 환경
이미지 : 소스 코드, 라이브러리, 종속성, 애플리케이션을 실행하는데 필요한 기타 파일을 포함하는 불편 파일(변경 불가)