- Published on
- •👁️
5월16일 기술 공부 - 기타
- Authors

- Name
- River
상세 설명
스택의 기본 개념
- LIFO(후입선출) 구조의 자료구조
- 나중에 들어간 데이터가 먼저 나온다.
- 기본 연산:
push(데이터 추가),pop(데이터 제거),peek(맨 위 데이터 조회) - 연산의 시간 복잡도는 대부분 O(1)
- 함수 호출 스택, 웹 브라우저 뒤로가기, 깊이 우선 탐색(DFS) 등에 활용됨
상세 설명
동시성(Concurrency)의 개념
- 여러 작업이 논리적으로 동시에 수행되는 것처럼 보이는 개념
- 하나의 코어가 여러 작업을 빠르게 전환하며 처리 (컨텍스트 스위칭)
- 사용자 입장에선 동시에 실행되는 것처럼 느껴짐
- 소프트웨어적 동시 처리
- Ex. 단일 스레드에서 I/O 작업 중 다른 작업 수행 (비동기)
동시성은 "동시에 진행되는 것처럼 보이는" 개념으로, 실제로는 빠른 작업 전환을 통해 이루어진다. 사용자 입장에서는 여러 작업이 동시에 진행되는 것처럼 느끼지만, 실제로는 한 번에 하나의 작업만 처리하고 있을 수 있다.
병렬성(Parallelism)의 개념
- 여러 작업이 물리적으로 동시에 실행되는 개념 (정확히 같은 시점)
- 멀티코어 CPU나 분산 시스템에서 각 작업을 동시에 실행
- 하드웨어적 실제 동시 처리
- 하드웨어적 지원이 필수적이다.
- Ex. 4개의 코어가 각각 다른 작업을 동시에 처리
List의 parallelStream()란?
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8);
int sum = numbers.parallelStream()
.filter(n -> n % 2 == 0)
.mapToInt(n -> n * n)
.sum();
- parallelStream()은 내부적으로 ForkJoinPool.commonPool을 사용해 작업을 멀티 스레드로 분산시켜 실행
- 즉, 사용자가 따로 스레드를 만들지 않아도 JVM이 멀티코어 환경을 활용하여 자동으로 병렬로 처리한다
- 하지만, 작은 데이터에서는
stream()보다 느릴 수 있다. (스레드에 관한 오버헤드)
parallelStream() 실행한다고 무조건 병렬로 처리하지 않을 수 있다. (항상 병렬 처리를 보장하지 않음)
컨텍스트 스위칭(Context Switching)
- CPU가 실행 중인 작업(스레드)의 상태(Context)를 저장하고, 다른 작업의 상태를 불러와 실행을 전환하는 과정을 말한다.
- 이 과정은 일정한 오버헤드가 발생하며, 스위칭이 잦아지면 성능 저하의 원인이 될 수 있다.
멀티 스레드를 쓰면 무조건 병렬성인가?
- 멀티 스레드를 사용한다고 해서 반드시 물리적인 병렬 처리(실제 동시에 실행)가 일어나는 것은 아니다.
- 이는 CPU의 코어 수와 스케줄링 방식에 따라 달라진다.
우리가 병렬로 실행되도록 요청하더라도, 실제로 병렬 처리가 될지는 CPU 스케줄러의 판단과 시스템 자원 상황에 따라 달라진다. 예를 들어, 4코어 CPU인데 동시에 10개의 스레드가 실행 중이면 4개만 병렬 실행하고 나머지 6개는 컨텍스트 스위칭으로 처리해야 한다.
상세 설명
GC(Garbage Collection) 알고리즘이란?
- 더 이상 사용되지 않는 객체를 자동으로 찾아서 메모리에서 제거하는 알고리즘
- 주요 알고리즘
- Serial GC, Parallel GC, CMS(Concurrent Mark and Sweep)
- G1 GC(Garbage First), ZGC(Z Garbage Collector), Shenandoah GC
- JVM은 GC를 통해 불필요한 객체를 자동으로 정리
- 이때 사용하는 알고리즘은 Java 9 이상인 경우 G1 GC를 기본값으로 사용한다.
Serial GC
- 단일 스레드로 GC 수행 (Minor / Full GC 모두)
- Stop-the-world 시간이 길고 병렬 처리 안 됨
- 클라이언트 애플리케이션이나 작은 Heap에서 유리
- 존재는 하지만, 거의 사용 안 함
- 사용 옵션:
XX:+UseSerialGC
Parallel GC (또는 Throughput GC)
- 여러 스레드로 GC를 병렬 수행 (주로 Minor GC)
- 전체 애플리케이션 처리량(Throughput)을 높이는 데 초점
- 잠깐 멈추지만 빠르게 정리해서 다시 실행 가능
- 사용 옵션:
XX:+UseParallelGC(Java 8의 기본값)
CMS (Concurrent Mark and Sweep)
- 응답 속도(지연 시간) 최소화 목표
- Stop-the-world 시간을 줄이기 위해 GC를 앱 스레드와 병행 실행
- 단계: 초기 마크 → 마크 → 리마크 → 스윕
- 단점: 프래그멘테이션(Heap 조각화), Full GC 느림
- 오래된 방식의 알고리즘으로 G1 GC가 모두 대체
- 사용 옵션:
XX:+UseConcMarkSweepGC(Java 9부터 deprecated, Java 14부터 제거됨)
G1 GC (Garbage First)
- 대규모 Heap에서 좋은 성능
- Heap을 여러 영역(region)으로 나눠서 관리
- 필요할 때마다 GC를 부분적으로 수행 → Full GC 적음 (짧은 STW)
- 낮은 지연 시간과 적당한 처리량의 균형
- 대부분의 서버 애플리케이션은 G1 GC로 충분
- 사용 옵션:
XX:+UseG1GC(Java 9 이후 기본값)
ZGC (Z Garbage Collector)
- 초저 지연 시간 (10ms 이하)을 목표로 함
- GC 중에도 대부분의 작업을 애플리케이션과 병행해서 수행
- 수 TB 단위의 대용량 Heap에서도 잘 동작
- 사용 옵션:
XX:+UseZGC(Java 11 이상)
Shenandoah GC
Red Hat에서 개발한 초저지연 GC
GC 시간 중 대부분의 작업을 애플리케이션과 병행 수행하며,
Stop-the-world 정지 시간을 sub-millisecond 수준(0.001초 미만)으로 최소화하는 것이 목표
G1처럼 region 기반 + 동시 compacting 지원
다만, G1은 compacting이 STW 동안 수행되지만, 여기서는 병렬 수행
사용 옵션:
XX:+UseShenandoahGC(Java 12 이상)
상세 설명
결합도(Coupling)
- 모듈 간의 의존 관계나 연결 정도를 의미
- 낮은 결합도(Low Coupling)는 다른 모듈의 변경에 영향을 덜 받는다는 의미로 변경에 강하고 테스트가 용이하다. 반대로 높은 결합도인 경우 변경에 취약해 유연성 낮아 유지보수가 어렵다
- Ex. Service가 Repository를 인터페이스로 주입받는 구조 ⇒ 낮은 결합도
낮은 결합도를 달성하기 위해 의존성 주입(DI), 인터페이스 활용, 추상화 등의 기법을 사용
이를 통해 한 모듈의 변경이 다른 모듈에 미치는 영향을 최소화할 수 있다.
응집도(Cohesion)
하나의 모듈이 얼마나 관련성 높은 기능들로 구성되어 있는가를 의미
높은 응집도(High Cohesion)는 하나의 책임/관심사에 집중된 모듈로 재사용성이 높고, 코드 이해도가 높다. 반면 낮은 응집도(Low Cohesion)인 경우 서로 다른 책임이 섞여 있어서 유지보수가 어렵다
Ex. 회원 관련 기능만 포함된 UserService는 높은 응집도
여기에 로그인, 결제, 이메일 발송 기능이 섞여 있다면 낮은 응집도
SOLID의 단일 책임 원칙(SRP)은 높은 응집도를 달성하기 위한 핵심 원칙이다.
하나의 클래스나 모듈은 변경의 이유가 오직 하나여야 한다.
결합도의 종류
- 내용 결합도(Content Coupling) : 한 모듈이 다른 모듈의 내부 구현에 직접 의존 (최악)
- 공통 결합도(Common Coupling) : 여러 모듈이 전역 데이터를 공유
- 제어 결합도(Control Coupling) : 한 모듈이 다른 모듈의 내부 논리 흐름을 제어
- 스탬프 결합도(Stamp Coupling) : 모듈 간에 자료 구조를 전달
- 데이터 결합도(Data Coupling) : 모듈 간에 매개변수를 통해 데이터만 주고받음 (가장 좋음)
응집도의 종류
- 우연적 응집도(Coincidental) : 관련 없는 기능들이 한 모듈에 모여 있음 (최악)
- 논리적 응집도(Logical) : 논리적으로 관련된 기능들이 한 모듈에 있음
- 시간적 응집도(Temporal): 특정 시점에 함께 실행되는 기능들의 모음
- 절차적 응집도(Procedural) : 특정 절차나 순서로 실행되는 기능들의 모음
- 교환적 응집도(Communicational) : 같은 입출력 데이터를 사용하는 기능들의 모음
- 순차적 응집도(Sequential) : 한 기능의 출력이 다른 기능의 입력이 되는 형태
- 기능적 응집도(Functional) : 단일 목적을 위한 모든 기능이 한 모듈에 있음 (가장 좋음)
실무에서의 응용
- Spring Framework : 의존성 주입(DI)으로 낮은 결합도를 구현
- 마이크로서비스 아키텍처(MSA) : 서비스 간 낮은 결합도, 서비스 내 높은 응집도 지향
- SOLID 원칙 : 특히 단일 책임 원칙(SRP)과 의존성 역전 원칙(DIP)이 응집도와 결합도에 관련
상세 설명
서버 컴포넌트(Server Component)란?
- React 컴포넌트 중 서버에서만 렌더링되는 컴포넌트
- 서버에서 실행되어 HTML만 생성한 후 클라이언트에 전달되며, JavaScript는 번들되지 않음
- React 18 + Next.js 13 이상에서 기본적으로 Server Component 방식을 사용
- 보안이 중요한 비즈니스 로직, DB 직접 호출 등에 적합
- 브라우저에서 실행되지 않기 때문에 JS 이벤트 핸들링 불가
서버 컴포넌트는 기존의 SSR(Server-Side Rendering)과는 다른 개념입니다. SSR은 초기 렌더링만 서버에서 수행하고 이후 JS가 클라이언트에 로드되어 "하이드레이션" 과정을 거치지만, 서버 컴포넌트는 아예 클라이언트에 JS 코드를 보내지 않는다.
서버 컴포넌트를 쓰면 좋은 상황
- DB에서 직접 데이터를 가져와 화면을 구성할 때 (API 호출 불필요)
- 인증/인가 로직 등 민감한 로직이 포함된 경우
- 첫 페이지 로딩 속도를 중요하게 여길 때
- JS 번들 크기를 최소화하고 싶을 때
- 클라이언트 컴포넌트와 혼합 사용하여 효율적 분리하고자 할 때
서버 컴포넌트에서는 useState, useEffect 같은 React 훅을 사용할 수 없으며, 브라우저 API에 접근할 수 없고, 이벤트 리스너를 추가할 수 없다. 이러한 기능이 필요한 경우 클라이언트 컴포넌트를 사용해야 한다.
클라이언트 컴포넌트와 서버 컴포넌트 비교
| 특성 | 서버 컴포넌트 | 클라이언트 컴포넌트 |
|---|---|---|
| 렌더링 위치 | 서버에서만 렌더링 | 클라이언트에서 렌더링 |
| JS 번들 포함 여부 | 미포함 (HTML만 전송) | 포함 |
| React 훅 사용 | 불가능 (useState 등) | 가능 |
| 이벤트 처리 | 불가능 | 가능 |
| 브라우저 API 접근 | 불가능 | 가능 |
| 서버 리소스 접근 | 직접 접근 가능 | 불가능 (API 필요) |
| 파일 정의 방식 | 기본 방식 | 'use client' 지시문 필요 |