2025-11-20
오늘 배운 것
- 멀티 프로레스 vs 멀티 스레딩
- 멀티 프로세스
- 프로세스를 여러 개 띄워서 작업을 병렬로 처리하는 방식
- 장점
- 프로세스마다 메모리가 완전히 분리되어 있어 안정성이 높음
- 한 프로세스가 죽어도 다른 프로세스에 영향이 적음
- 단점
- 컨텍스트 스위칭 비용이 큼
- 메모리를 중복해서 사용하기 때문에 리소스 사용량 증가
- 멀티 스레딩
- 하나의 프로세스 안에서 스레드를 여러 개 만들어 자원을 공유하며 동시에 실행하는 방식
- 장점
- 스레드들이 같은 메모리를 공유하기 때문에 더 가볍고 빠르게 동작
- 컨텍스트 스위칭 비용이 작음
- 따라서 IO 바운드 작업 / 대량 요청 처리 서버에서 자주 사용
- 단점
- 자원을 공유하기 때문에 동기화 문제, 데드락 등 안정성 이슈 관리 필요
- 멀티 프로세스
- 뮤텍스 vs 세마포어
- 둘다 공유 자원 접근을 제어하는 동기화 기법
- 뮤텍스 (문 잠금 열쇠)
- 오직 1개만 접근하도록 보장하는 완전한 상호 배제 락
- 하나의 스레드만 락을 가질 수 있고
- 락을 획득한 스레드만 해제할 수 있음
- 임계 구역을 하나의 스레드만 안전하게 보호해야할 때 사용
- 세마포어 (입장 인원 제한)
- 허용 가능한 스레드 수를 0 ~ N개로 조절하는 카운팅 기반 동기화 기법
- DB 커넥션 풀처럼 최대 N개까지 동시에 접근 허용 해야 하는 경우 사용
- 획득한 스레드가 아닌 다른 스레드도 해제할 수 있음
- url 입력하면 벌어지는 일
- 브라우저가 URL 분석하여 프로토콜/호스트/포트/패스 등을 구분
- 브라우저 캐시, DNS 캐시, OS 캐시 등에 요청한 리소스가 있는지 먼저 확인
- 캐시에 없다면 DNS 서버에 도메인을 실제 IP 로 변환
- 로컬 → OS → 라우터 → ISP DNS 순서로 캐시를 확인하면서 조회
- IP를 얻으면 서버와 TCP 연결
- HTTPS라면 TLS 인증서 교환, 대칭키 생성해서 암호화된 연결 만듬
- 브라우저가 서버로 GET 요청 보냄
- 서버는 라우팅 → 컨트롤러 → 서비스 → DB 조회등의 과정을 거쳐 요청한 HTML, JSON, 리소스를 생성해 응답
- 브라우저는 응답을 받고 HTML, CSS 파싱하고 JS 실행하며 렌더 트리를 만들고 화면에 그림
- 추가 리소스가 필요하다면 또다시 병렬 요청해서 받아옴
- 해시테이블
- 개념
- 키를 해시 함수를 이용해 숫자로 변환해 배열 인덱스에 매핑해 저장하는 자료 구조 입니다.
- 키를 직접 비교하지 않고 해시 값을 이용해 접근하므로 평균적으로 매우 빠른 탐색 성능을 냅니다.
- 평균적으로 O(1)이고 최악은 O(N)입니다.
- 최악이 O(N)인 경우는
- 많은 키가 동일한 버킷으로 몰리는 해시 충돌이 심할 때
- 체이닝의 경우 연결 리스트가 길어지면 탐색이 O(N)
- 오픈 어드레싱은 연속 프루빙이기 때문에 성능 급락
- 충돌
- 체이닝
- 버킷 하나에 연결 리스트나 트리 형태로 여러 개의 엔트리를 저장하는 방식
- 같은 인덱스로 해시된 값들은 같은 버킷 리스트에 모여 저장
- 장점
- 구현이 쉽고 테이블이 꽉차도 삽입 가능
- 평균적으로 O(1)
- 충돌이 많아지면 트리화 해서 O(logN)까지 관리 가능
- 자바 8 해시맵 로드 팩터가 특정 기준점 이상 되면 레드 블랙 트리로 전환
- 단점
- 메모리 사용이 증가
- 편향되면 O(N)
- 오픈 어드레싱
- 충돌이 나면 다른 빈 버킷을 탐색해서 넣는 방식
- 모든 데이터가 하나의 테이블 안에 들어가고 별도의 리스트 없음
- 대표적인 프루빙 방식
- 선형 프루빙 : +1씩 순서대로 탐색
- 쿼드래틱 프루빙 : 제곱 단위로 간격 띄워 탐색
- 더블 해싱 : 2차 해시 함수로 점프 간격 결정
- 장점
- 메모리 효율적
- 캐시 적중률이 좋아 일부 상황에서 빠름
- 단점
- 적절한 로드 팩터 관리가 매우 중요
- 테이블이 꽉차면 성능 급락 → 클러스터링 발생
- 체이닝
- 공격자가 해시 함수 알면?
- 해시 함수가 알려지면 공격자가 의도적으로 충돌을 유발해 해시 테이블의 worst-case O(N) 동작을 강제로 만들어 DoS를 일으킬 수 있습니다. 그래서 실제 시스템은 시드 랜덤화를 적용하거나 충돌 버킷을 트리화하는 방식으로 방어합니다
- 개념
- 해시 테이블 해시 맵은 멀티 스레드 환경에서 어떻게 되나요
- HashMap은 멀티스레드 환경에서 스레드 안전하지 않고,특히 Java 7 이전에는 resize 과정에서 cycle이 생겨 무한 루프가 발생할 수도 있습니다.
- 이런 문제를 방지하기 위해 ConcurrentHashMap을 사용해서 락 경합을 줄이고 안전하게 동시성을 보장 합니다.”
- concurrentHashMap
- put/write : CAS + 필요할 때만 버킷 단위 synchronized
- compare and swap
- 락을 걸지 않고도 공유 데이터를 안전하게 변경하기 위해 사용하는 원자적 연산
- 내가 예상한 값과 같은지 비교후 같다면 교체
- 실패한 스레드만 다시 시도하면 되므로 락 프리 알고리즘을 만들 수 있음
- 단점 : A→ B → A 가 되면 그 값이 안변했다고 착각
- 이를 해결하기 위해 버전 넘버나 스탬프드 레퍼런스 사용
- compare and swap
- read/get : 거의 락 프리
- 전체 맵에 락을 거는 해시 테이블이나 synchronizedMap과 달리 필요한 곳에 정교하게 락을 걸어 동시성 성능이 좋음
- 해시 테이블은 메서드마다 전체 synchronized
- put/write : CAS + 필요할 때만 버킷 단위 synchronized
- SOLID
- 객체지향 설계의 다섯가지 핵심 원칙으로 코드의 유지보수성, 확장성, 유연성을 높이기 위해 사용
- SRP (Single Responsibility Principle)
- 하나의 클래스는 오직 하나의 이유로만 변경되어야 함
- 여러 책임을 갖고 있으면 다양한 이유로 변경이 잦아지고 테스트가 어려워짐
- 예 : 컨트롤러가 로직, db, 검증 다하기 → SRP 위반
- OCP (Open/Closed Principle)
- 확장에는 열려있고 수정에는 닫혀있기
- 기존코드를 수정하지 않고 기능 추가
- 전략 패턴 예시
- LSP (Liskov Substitution Principle)
- 자식 클래스는 부모 클래스로 교체 가능해야함
- 상위 타입으로 다형성을 사용해도 정상 동작 해야함
- ISP (Interface Segregation Principle)
- 자기에게 필요없는 메서드를 의존하면 안됨
- 너무 큰 인터페이스보단 작은 인터페이스 여러개로 분리
- 예 :동물 인터페이스에 날기 수영하기 달리기 모두 넣는것
- DIP (Dependency Inversion Principle)
- 고수준 모듈은 저수준 모듈에 직접 의존하면 안되고 둘다 추상화 즉 인터페이스에 의존해야함
- 구현체 교체가 쉬워지고 유연한 구조가 됨
- 이를 활용한 것이 스프링의 DI
-
분산 락
멀티 서버 환경에서 공유 자원 접근을 제어하기 위해 사용하는 것이 분산 락입니다. 자바의 synchronized는 프로세스 내부에서만 유효하기 때문에 서버가 여러 대이면 외부 스토리지(주로 Redis)를 이용해 락을 잡습니다. Redis는 SETNX로 락을 만들고 TTL로 데드락을 방지하며, 작업 완료 시 삭제하는 방식입니다. DB의 FOR UPDATE나 ZooKeeper를 이용한 순서 기반 락도 있지만, 실무에서는 Redis 기반 락이 가장 많이 사용됩니다.
- DI IoC
- IoC
- 제어의 역전은 객체 생성, 의존성 주입, 생명주기를 개발자가 아닌 컨테이너가 관리하는것
- DI 방식
- IoC의 한 형태로 객체가 필요로 하는 의존성을 스스로 만들지 않고 외부에서 주입 받는 기법
- 생성자 주입
- final이 가능해 불변성 보장
- 컴파일 시점에 순환참조 파악 가능
- 테스트 용이
- 필드 주입
- 간단함
- 불변성 없음
- 순환참조 발견 어려움
- 테스트 불편 목 주입이 어려움
- 세터 주입
- 런타임에 교체 가능
- 선택적 의존성이 필요할 때 가끔 사용
- 필수 의존성 주입에는 부적절
- IoC
- JPA N+1
- JPA에서 연관된 엔티티를 조회할 때 첫번재 쿼리로 N개의 부모 엔티티를 가져오고, 각 부모의 연관된 자식 엔티티를 조회하기 위해 추가로 N번의 쿼리가 실행되는 문제
- JPA 기본 fetch 전략 들 중 특히 oneToMany 관계에 레이지 로딩이 걸려 있으면 부모 먼저 조회한 뒤 자식 접근할 때 매번 쿼리 발생
- 해결방법
- fetch join으로 한번의 쿼리로 가져오기
- 페이징 불가능
- 배치 사이즈 옵션을 설정해서 레이지 로딩 유지하면서 in 쿼리로 묶어서 한번에 조회
- IN 쿼리에 들어갈 수 있는 최대 파라미터
- DB 마다 다름 보통 unsigned integer의 최대값인 65,535개정도
- IN 쿼리에 들어갈 수 있는 최대 파라미터
- fetch join으로 한번의 쿼리로 가져오기
- JPA, Jdbc 차이
- JDBC
- DB와 직접 통신하는 가장 낮은 수준의 API로
- sql 부터 커넥션, preparedStatement, 바인딩, 실행, resultset 파싱, 예외처리, 자원해제까지의 모든 절차를 개발자가 진행해야함
- 장점
- 성능이 예측 가능하고
- DB 쿼리를 완전히 개발자가 컨트롤
- 튜닝/특수 SQL 사용에 유리
- 단점
- 반복코드 많고
- 생산성이 낮고
- 객체와 SQL 결과 매핑이 불편
- 유지보수 어려움
- JPA
- ORM 기술로, SQL을 직접 다루지 않고 객체 중심으로 db 조작할 수 있게 해줌
- 엔티티 클래스로 매핑하고, 메서드 호출로 persist, update, remove 등 처리
- sql은 jpa가 자동생성
- 장점
- 생산성, 유지보수성 증가
- 엔티티 중심 도메인 설계 가능
- 캐싱, 지연로딩, 변경 감지 등 강력
- 트랜잭션 관리 자동화
- 단점
- 러닝 커브 존재
- 복잡한 SQL에서는 오히려 비효율
- N+1 같은 성능 문제 고려해야함
-
“JDBC는 SQL 중심, JPA는 객체 중심입니다.
JDBC는 모든 DB 작업을 직접 처리해야 하지만,
JPA는 ORM으로 SQL 생성·매핑·트랜잭션 등을 자동으로 처리해
생산성과 유지보수성이 크게 높아집니다.”
JPA는 ORM 계층이고 실제 SQL 실행은 JDBC 드라이버가 합니다.
- JDBC
- 스프링 필터, 인터셉터
- 필터
- 가장 앞단에서 디스패처 서블릿 이전에 요청을 처리하는 서블릿 레벨 컴포넌트
- 공통 인코딩, 보안, 인증/토큰 검증, 전체 요청에 대한 전처리
- 디스패처 서블릿
- 스프링 MVC 중심 허브로, 요청을 컨트롤러로 연결하는 프론트 컨트롤러
- 요청을 핸들러매핑으로 보내서 어떤 컨트롤러가 처리할지 결정하고 핸들러 어댑터가 실제 메서드 실행
- 뷰 리졸버로 응답 렌더링
- 인터셉터
- 디스패처 서블릿 이후, 컨트롤러 호출 직전,후
- 컨트롤러 실행흐름 제어하기 위해 사용하는 스프링이 제공하는 MVC 레벨의 확장 지점
- 파라미터 접근이랑 DI 가능
- 인증/인가, 공통 로깅, 요청시간 측정, 사용자 권한 체크
- AOP
- 컨트롤러를 넘어서 서비스/레포지토리 메서드 레벨에서 동작
- 프록시 기반으로 메서드 실행 전/후/예외를 가로챔
- 비즈니스 로직과 횡단 관심사 분리
- 트랜잭션/로깅/성능 모니터링 등 메서드 중심 처리에 강함
- 인증은 반드시 Filter에서 해야 합니다.
Servlet 단계에서 요청을 차단해야 성능 낭비를 막고 안정적이기 때문입니다.
반면 인가는 이미 인증된 사용자에 대해
요청의 흐름(Controller)을 기준으로 판단하므로 Interceptor가 적합하고,
도메인 규칙 기반의 인가는 AOP로 처리하는 것이 더 자연스럽습니다.”
- 필터