개발자 필수 기술 개념 완벽 가이드
📡 네트워크
OSI 7계층
OSI(Open Systems Interconnection) 7계층 모델은 네트워크 통신을 7개의 계층으로 나누어 표준화한 모델입니다.
각 계층의 역할:
- 물리 계층(Physical Layer): 전기적 신호를 통한 비트 전송
- 데이터링크 계층(Data Link Layer): 프레임 단위로 데이터 전송, 오류 검출/수정
- 네트워크 계층(Network Layer): IP 주소를 이용한 라우팅
- 전송 계층(Transport Layer): TCP/UDP를 통한 포트 간 통신
- 세션 계층(Session Layer): 세션 관리 및 동기화
- 표현 계층(Presentation Layer): 데이터 암호화, 압축, 형식 변환
- 응용 계층(Application Layer): HTTP, FTP 등 사용자 서비스
웹 통신 흐름
웹 브라우저가 서버와 통신하는 전체 과정:
- URL 입력: 사용자가 브라우저에 URL 입력
- DNS 조회: 도메인명을 IP 주소로 변환
- TCP 연결: 3-way handshake를 통한 연결 설정
- HTTP 요청: GET/POST 등의 HTTP 요청 전송
- 서버 처리: 웹서버가 요청을 처리하고 응답 생성
- HTTP 응답: HTML, CSS, JS 등의 응답 전송
- 렌더링: 브라우저가 응답을 파싱하여 화면에 표시
TCP와 UDP
TCP (Transmission Control Protocol):
- 연결지향적 프로토콜
- 신뢰성 보장 (데이터 순서, 무결성 보장)
- 흐름제어와 혼잡제어 지원
- 3-way handshake로 연결 설정
- 오버헤드가 큼
UDP (User Datagram Protocol):
- 비연결성 프로토콜
- 빠른 전송 속도
- 신뢰성 보장하지 않음
- 실시간 스트리밍, DNS 조회 등에 사용
- 오버헤드가 작음
HTTP와 HTTPS
HTTP (HyperText Transfer Protocol):
- 웹에서 데이터를 주고받는 프로토콜
- 평문으로 데이터 전송 (보안 취약)
- 기본 포트: 80
HTTPS (HTTP Secure):
- HTTP + SSL/TLS 암호화
- 데이터 암호화로 보안성 향상
- 인증서를 통한 서버 신원 확인
- 기본 포트: 443
HTTP 버전별 특징
HTTP/1.0:
- 각 요청마다 새로운 TCP 연결
- Keep-Alive 헤더로 연결 유지 가능
HTTP/1.1:
- 지속적 연결 (Persistent Connection) 기본 지원
- 파이프라이닝 지원
- 청크 전송 인코딩
HTTP/2:
- 바이너리 프로토콜
- 멀티플렉싱 지원
- 서버 푸시 기능
- 헤더 압축 (HPACK)
HTTP/3:
- UDP 기반 (QUIC 프로토콜)
- 더 빠른 연결 설정
- 패킷 손실에 강함
REST API
REST(Representational State Transfer)는 웹 서비스 설계 아키텍처 스타일입니다.
REST 원칙:
- 무상태성(Stateless): 각 요청은 독립적
- 캐시 가능(Cacheable): 응답 데이터는 캐시 가능해야 함
- 계층화(Layered System): 클라이언트는 서버 구조를 알 필요 없음
- 통일된 인터페이스(Uniform Interface): 일관된 방식으로 리소스 접근
- 자원의 식별: URI로 리소스 식별
- 표현을 통한 자원 조작: HTTP 메서드 사용
HTTP 메서드:
- GET: 조회
- POST: 생성
- PUT: 전체 수정
- PATCH: 부분 수정
- DELETE: 삭제
CORS (Cross-Origin Resource Sharing)
다른 출처(Origin)에서 리소스에 접근할 수 있도록 하는 HTTP 헤더 기반 메커니즘입니다.
Origin 구성 요소:
- Protocol (http/https)
- Domain (example.com)
- Port (3000)
CORS 해결 방법:
- 서버에서 Access-Control-Allow-Origin 헤더 설정
- 프록시 서버 사용
- JSONP 사용
로드밸런싱
여러 서버에 트래픽을 분산시켜 성능과 가용성을 향상시키는 기술입니다.
로드밸런싱 알고리즘:
- 라운드 로빈: 순차적으로 분배
- 가중 라운드 로빈: 서버 성능에 따른 가중치 적용
- 최소 연결: 연결 수가 가장 적은 서버로 분배
- IP 해시: 클라이언트 IP를 해시하여 분배
로드밸런서 종류:
- L4 로드밸런서: IP, Port 기반
- L7 로드밸런서: HTTP 헤더, URL 기반
🖥️ 운영체제
프로세스와 스레드
프로세스 (Process):
- 실행 중인 프로그램의 인스턴스
- 독립적인 메모리 공간 할당
- 프로세스 간 통신(IPC) 필요
- 생성/종료 비용이 높음
스레드 (Thread):
- 프로세스 내에서 실행되는 실행 단위
- 같은 프로세스의 메모리 공간 공유
- 가벼운 프로세스 (Light-weight Process)
- 빠른 생성/전환 가능
멀티프로세싱 vs 멀티스레딩:
- 멀티프로세싱: 안정성 높음, 메모리 사용량 많음
- 멀티스레딩: 효율적, 동기화 문제 발생 가능
컨텍스트 스위칭 오버헤드 줄이는 방법
컨텍스트 스위칭이란: CPU가 현재 실행 중인 프로세스/스레드를 다른 것으로 교체하는 과정
오버헤드 줄이는 방법:
- 스레드 풀 사용: 스레드 재사용으로 생성/소멸 비용 절약
- 코루틴/비동기 프로그래밍: 논블로킹 I/O 사용
- CPU 친화도 설정: 프로세스를 특정 CPU 코어에 고정
- 적절한 우선순위 설정: 불필요한 컨텍스트 스위칭 방지
- 시스템 콜 최소화: 커널 모드 전환 횟수 줄이기
가상메모리
물리 메모리 크기의 한계를 극복하고 메모리 관리를 효율적으로 하는 기술입니다.
주요 개념:
- 가상주소: 프로세스가 사용하는 논리적 주소
- 물리주소: 실제 메모리의 주소
- 페이지: 가상메모리를 일정한 크기로 나눈 단위
- 프레임: 물리메모리를 페이지와 같은 크기로 나눈 단위
페이지 교체 알고리즘:
- FIFO: 먼저 들어온 페이지를 먼저 교체
- LRU: 가장 오래 사용되지 않은 페이지 교체
- LFU: 가장 적게 사용된 페이지 교체
TLB 플러시
TLB(Translation Lookaside Buffer)는 가상주소를 물리주소로 변환하는 정보를 캐시하는 하드웨어입니다.
TLB 플러시가 발생하는 경우:
- 컨텍스트 스위칭 시
- 페이지 테이블 변경 시
- 커널 모드 전환 시
성능 영향:
- TLB 미스 발생 시 메모리 접근 시간 증가
- 캐시 지역성 고려한 프로그래밍 중요
CPU 캐시
CPU와 메인 메모리 간의 속도 차이를 줄이기 위한 고속 저장장치입니다.
캐시 계층:
- L1 캐시: CPU 코어 내부, 가장 빠름
- L2 캐시: 코어별 또는 공유
- L3 캐시: 모든 코어가 공유
캐시 최적화 기법:
- 지역성 원리 활용 (시간적/공간적 지역성)
- 캐시 친화적인 데이터 구조 사용
- 루프 타일링, 블로킹 기법
데드락 (교착상태)
두 개 이상의 프로세스가 서로의 자원을 기다리며 무한히 대기하는 상태입니다.
데드락 발생 조건 (4가지 모두 만족):
- 상호배제: 자원을 동시에 사용할 수 없음
- 점유대기: 자원을 점유한 채 다른 자원 대기
- 비선점: 강제로 자원을 뺏을 수 없음
- 순환대기: 프로세스들이 원형으로 자원 대기
데드락 해결 방법:
- 예방: 4가지 조건 중 하나를 차단
- 회피: 안전한 상태만 유지 (Banker's Algorithm)
- 탐지: 데드락 발생 후 탐지하여 해결
- 무시: 발생 확률이 낮으면 무시
유저모드와 커널모드
유저모드 (User Mode):
- 응용 프로그램이 실행되는 모드
- 제한된 권한으로 실행
- 하드웨어 직접 접근 불가
- 안전하지만 기능 제한적
커널모드 (Kernel Mode):
- 운영체제가 실행되는 모드
- 모든 하드웨어 접근 가능
- 시스템 자원에 대한 완전한 제어
- 강력하지만 위험함
모드 전환:
- 시스템 콜, 인터럽트, 예외 발생 시
- 모드 전환 비용 발생
📊 자료구조와 알고리즘
선택정렬 (Selection Sort)
가장 작은(큰) 원소를 선택하여 맨 앞으로 이동시키는 정렬 알고리즘입니다.
시간복잡도: O(n²) 공간복잡도: O(1) 특징: 단순하지만 비효율적, 불안정 정렬
삽입정렬 (Insertion Sort)
정렬된 부분에 새 원소를 올바른 위치에 삽입하는 정렬 알고리즘입니다.
시간복잡도: 최선 O(n), 평균/최악 O(n²) 공간복잡도: O(1) 특징: 안정 정렬, 거의 정렬된 배열에 효율적
버블정렬 (Bubble Sort)
인접한 두 원소를 비교하여 순서가 잘못되면 교환하는 정렬 알고리즘입니다.
시간복잡도: O(n²) 공간복잡도: O(1) 특징: 구현이 간단, 효율성이 떨어짐, 안정 정렬
퀵정렬 (Quick Sort)
피벗을 선택하여 분할정복으로 정렬하는 알고리즘입니다.
시간복잡도: 평균 O(n log n), 최악 O(n²) 공간복잡도: O(log n) 특징: 실제로 가장 빠름, 불안정 정렬
병합정렬 (Merge Sort)
배열을 반으로 나누어 정렬 후 병합하는 분할정복 알고리즘입니다.
시간복잡도: O(n log n) - 모든 경우 공간복잡도: O(n) 특징: 안정 정렬, 예측 가능한 성능
힙정렬 (Heap Sort)
힙 자료구조를 이용한 정렬 알고리즘입니다.
시간복잡도: O(n log n) 공간복잡도: O(1) 특징: 불안정 정렬, 최악의 경우에도 O(n log n) 보장
🗄️ 데이터베이스
인덱싱
데이터베이스에서 검색 속도를 향상시키기 위해 사용하는 자료구조입니다.
인덱스 종류:
- B-Tree 인덱스: 가장 일반적, 범위 검색에 효율적
- Hash 인덱스: 등가 검색에 빠름
- Bitmap 인덱스: 카디널리티가 낮은 컬럼에 효과적
인덱스 장단점:
- 장점: 검색 속도 향상, 정렬된 결과 제공
- 단점: 저장공간 추가 필요, 삽입/수정/삭제 성능 저하
멱등성의 의미
같은 연산을 여러 번 수행해도 결과가 동일한 성질을 의미합니다.
HTTP 메서드별 멱등성:
- GET: 멱등 (조회만 함)
- POST: 비멱등 (매번 새로운 리소스 생성)
- PUT: 멱등 (전체 리소스 교체)
- DELETE: 멱등 (이미 삭제된 것을 다시 삭제해도 같은 결과)
트랜잭션
데이터베이스의 상태를 변화시키는 하나의 논리적 기능을 수행하기 위한 작업의 단위입니다.
ACID 속성:
- 원자성 (Atomicity): 모두 성공하거나 모두 실패
- 일관성 (Consistency): 데이터베이스 규칙 준수
- 격리성 (Isolation): 동시 실행되는 트랜잭션들이 서로 영향을 주지 않음
- 영속성 (Durability): 완료된 트랜잭션의 결과는 영구적으로 반영
격리 수준 (Isolation Level):
- READ UNCOMMITTED: 가장 낮은 격리 수준
- READ COMMITTED: 커밋된 데이터만 읽기
- REPEATABLE READ: 트랜잭션 내에서 일관된 읽기
- SERIALIZABLE: 가장 높은 격리 수준
샤딩과 파티셔닝
샤딩 (Sharding):
- 데이터를 여러 데이터베이스에 분산 저장
- 수평적 확장
- 샤드 키를 기준으로 분할
파티셔닝 (Partitioning):
- 하나의 데이터베이스 내에서 테이블 분할
- 수직 파티셔닝: 컬럼 기준 분할
- 수평 파티셔닝: 로우 기준 분할
ORM (Object-Relational Mapping)
객체와 관계형 데이터베이스의 테이블을 매핑해주는 기술입니다.
장점:
- 객체 지향적 코드 작성 가능
- 재사용성과 유지보수성 향상
- DBMS에 독립적
단점:
- 복잡한 쿼리 작성 어려움
- 성능 저하 가능성
- 러닝 커브 존재
AutoIncrement
데이터베이스에서 새로운 레코드 삽입 시 자동으로 증가하는 값을 생성하는 기능입니다.
특징:
- 주키(Primary Key)로 많이 사용
- 유니크한 값 보장
- 데이터베이스별로 구현 방식 다름 (MySQL: AUTO_INCREMENT, PostgreSQL: SERIAL)
🏗️ 아키텍처
WAS와 WS
WS (Web Server):
- 정적 컨텐츠 처리 (HTML, CSS, JS, 이미지)
- Apache, Nginx
- 80/443 포트 사용
WAS (Web Application Server):
- 동적 컨텐츠 처리 (비즈니스 로직 실행)
- Tomcat, JBoss
- DB 연결, 트랜잭션 관리
분리하는 이유:
- 역할 분담으로 성능 최적화
- 로드밸런싱 및 장애 분산
- 보안성 향상
MSA (Microservices Architecture)
애플리케이션을 작은 서비스들로 분해하여 개발/배포하는 아키텍처 패턴입니다.
특징:
- 서비스별 독립적인 개발/배포
- 폴리글랏 프로그래밍 가능
- 느슨한 결합, 높은 응집도
장점:
- 확장성과 유연성
- 기술 스택 자유도
- 장애 격리
단점:
- 복잡한 분산 시스템 관리
- 네트워크 통신 오버헤드
- 데이터 일관성 문제
이벤트기반 아키텍처
시스템 구성요소 간의 통신을 이벤트를 통해 수행하는 아키텍처 패턴입니다.
구성요소:
- Event Producer: 이벤트 발생
- Event Router: 이벤트 전달
- Event Consumer: 이벤트 처리
장점:
- 느슨한 결합
- 확장성과 유연성
- 비동기 처리 가능
멀티모듈
하나의 프로젝트를 여러 모듈로 나누어 관리하는 방식입니다.
장점:
- 코드 재사용성
- 의존성 관리 용이
- 빌드 시간 단축 가능
- 모듈별 독립적인 개발
DevOps
Development + Operations의 합성어로, 개발과 운영의 협업을 강화하는 문화와 방법론입니다.
핵심 원칙:
- 자동화 (Automation)
- 협업 (Collaboration)
- 지속적 개선 (Continuous Improvement)
- 고객 중심 (Customer-Centric)
CI/CD
지속적 통합(Continuous Integration)과 지속적 배포(Continuous Delivery/Deployment)입니다.
CI (Continuous Integration):
- 코드 변경사항을 자주 통합
- 자동화된 빌드와 테스트
- 빠른 피드백 제공
CD (Continuous Delivery/Deployment):
- 자동화된 배포 파이프라인
- 언제든 배포 가능한 상태 유지
- 배포 위험 최소화
무중단 배포 전략
-
Blue-Green 배포:
- 두 개의 동일한 환경 운영
- 트래픽을 한 번에 전환
-
Rolling 배포:
- 서버를 하나씩 순차적으로 배포
- 무중단이지만 두 버전이 동시 실행
-
Canary 배포:
- 일부 사용자에게만 새 버전 제공
- 점진적으로 확대
API Gateway
마이크로서비스 아키텍처에서 클라이언트와 서비스 간의 중간 계층 역할을 하는 구성요소입니다.
주요 기능:
- 라우팅
- 인증/인가
- 부하 분산
- 로깅/모니터링
- 요청/응답 변환
RabbitMQ와 Kafka
RabbitMQ:
- AMQP 프로토콜 기반
- 메시지 브로커
- 복잡한 라우팅 지원
- 트랜잭션 지원
Kafka:
- 분산 스트리밍 플랫폼
- 높은 처리량과 확장성
- 메시지 영구 저장
- 파티셔닝 지원
☕ Java/Spring
Garbage Collector
자바에서 사용하지 않는 객체를 자동으로 메모리에서 해제하는 기능입니다.
GC 알고리즘:
- Serial GC: 단일 스레드
- Parallel GC: 멀티 스레드
- G1 GC: 낮은 지연시간
- ZGC/Shenandoah: 초저지연 GC
GC 영역:
- Young Generation: Eden, Survivor
- Old Generation: 장기간 살아남은 객체
Java 버전별 특징
Java 8:
- Lambda 표현식
- Stream API
- Optional 클래스
- 인터페이스 default 메서드
Java 11:
- var 키워드 확장
- HTTP Client API
- String 메서드 추가
Java 17:
- Record 클래스
- Switch 표현식 개선
- Text Blocks
Interface와 Abstract의 차이
Interface:
- 다중 상속 가능
- 모든 메서드가 public abstract (Java 8부터 default 메서드 가능)
- 상수만 정의 가능
- implements 키워드로 구현
Abstract Class:
- 단일 상속만 가능
- 추상 메서드와 구현된 메서드 모두 가능
- 인스턴스 변수 정의 가능
- extends 키워드로 상속
equals()와 hashCode()
equals():
- 객체의 내용이 같은지 비교
- 기본적으로 == 연산자와 같음 (주소 비교)
- 오버라이드하여 논리적 동등성 구현
hashCode():
- 객체의 해시 코드 반환
- HashMap, HashSet 등에서 사용
- equals()를 오버라이드하면 hashCode()도 오버라이드해야 함
계약:
- equals()가 true이면 hashCode() 값이 같아야 함
- hashCode()가 같다고 equals()가 true는 아님
HashTable
키-값 쌍을 저장하는 자료구조로, 해시 함수를 사용하여 빠른 검색을 제공합니다.
특징:
- 평균 O(1) 시간복잡도
- 해시 충돌 처리 필요 (체이닝, 오픈 어드레싱)
- 동적 크기 조절 (리해싱)
Spring과 Spring Boot 차이
Spring Framework:
- 의존성 주입, AOP 등 핵심 기능
- 복잡한 설정 필요
- XML 또는 어노테이션 기반 설정
Spring Boot:
- Spring 기반의 개발 플랫폼
- 자동 설정 (Auto Configuration)
- 내장 서버 제공
- 스타터 의존성으로 간편한 의존성 관리
IoC (Inversion of Control)
제어의 역전으로, 객체의 생성과 의존성 관리를 프레임워크가 담당하는 원칙입니다.
특징:
- 객체 간 결합도 감소
- 테스트 용이성 향상
- 유연한 코드 구조
DI (Dependency Injection)
IoC를 구현하는 방법 중 하나로, 의존성을 외부에서 주입받는 패턴입니다.
주입 방법:
- 생성자 주입: 가장 권장되는 방식
- Setter 주입: 선택적 의존성에 사용
- 필드 주입: 테스트하기 어려움
Bean
Spring IoC 컨테이너가 관리하는 객체입니다.
특징:
- Spring 컨테이너에 의해 생성, 관리, 소멸
- 기본적으로 싱글톤
- @Component, @Service, @Repository 등으로 등록
Servlet
자바 웹 애플리케이션에서 동적 웹 페이지를 생성하는 서버측 프로그램입니다.
라이프사이클:
- init(): 서블릿 초기화
- service(): 요청 처리
- destroy(): 서블릿 종료
Spring MVC
Spring MVC는 Spring Framework의 웹 모듈로, Model-View-Controller 패턴을 기반으로 한 웹 애플리케이션 프레임워크입니다.
주요 구성 요소
- DispatcherServlet: 모든 HTTP 요청을 받는 프론트 컨트롤러
- HandlerMapping: URL과 컨트롤러 메소드를 매핑
- Controller: 비즈니스 로직을 처리하는 컴포넌트
- ViewResolver: 뷰 이름을 실제 뷰로 변환
- Model: 컨트롤러와 뷰 간의 데이터 전달 객체
동작 과정
- 클라이언트 요청이 DispatcherServlet에 전달
- HandlerMapping이 적절한 컨트롤러를 찾음
- 컨트롤러가 비즈니스 로직을 실행하고 ModelAndView 반환
- ViewResolver가 뷰를 찾고 모델 데이터와 함께 렌더링
- 최종 응답을 클라이언트에게 전송
Filter와 Interceptor
웹 애플리케이션에서 공통 관심사를 처리하는 두 가지 메커니즘입니다.
Filter (서블릿 필터)
- 위치: 서블릿 컨테이너 레벨에서 동작
- 시점: 서블릿 실행 전후
- 용도: 인코딩, 보안, 로깅 등
java@Component
public class LoggingFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
System.out.println("Before servlet execution");
chain.doFilter(request, response);
System.out.println("After servlet execution");
}
}
@Component
public class LoggingFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
System.out.println("Before servlet execution");
chain.doFilter(request, response);
System.out.println("After servlet execution");
}
}
Interceptor (스프링 인터셉터)
- 위치: Spring MVC 레벨에서 동작
- 시점: 컨트롤러 실행 전후
- 용도: 인증, 권한 체크, 공통 로직 처리
java@Component
public class AuthInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
Object handler) throws Exception {
// 컨트롤러 실행 전 처리
return true;
}
}
@Component
public class AuthInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
Object handler) throws Exception {
// 컨트롤러 실행 전 처리
return true;
}
}
실행 순서
Filter → DispatcherServlet → Interceptor → Controller
쿠키, 세션, 토큰
쿠키 (Cookie)
클라이언트 측에 저장되는 작은 데이터 조각으로, HTTP 상태를 유지하기 위해 사용됩니다.
특징:
- 클라이언트에 저장되어 보안에 취약
- 만료 시간 설정 가능
- 도메인과 경로 제한 가능
- 크기 제한: 4KB
세션 (Session)
서버 측에서 사용자 상태 정보를 관리하는 메커니즘입니다.
특징:
- 서버 메모리에 저장되어 상대적으로 안전
- 세션 ID로 클라이언트와 연결
- 서버 재시작 시 데이터 소실 가능
- 메모리 사용량 증가
토큰 (Token)
인증과 권한 부여를 위한 디지털 자격 증명입니다.
JWT (JSON Web Token):
- Header.Payload.Signature 구조
- 상태를 저장하지 않는 stateless 방식
- 자체 포함적 (self-contained)
- 확장성이 좋음
// JWT 예시
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.
SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
캐싱
데이터를 임시로 저장하여 성능을 향상시키는 기술입니다.
캐싱 레벨
- 브라우저 캐싱: 클라이언트 측 캐싱
- CDN 캐싱: 지리적으로 분산된 캐시
- 웹 서버 캐싱: 정적 파일 캐싱
- 애플리케이션 캐싱: 메모리 기반 캐싱
- 데이터베이스 캐싱: 쿼리 결과 캐싱
Spring Cache
java@Service
public class UserService {
@Cacheable("users")
public User findById(Long id) {
// DB에서 사용자 조회
return userRepository.findById(id);
}
@CacheEvict("users")
public void updateUser(User user) {
// 사용자 업데이트 후 캐시 제거
}
}
@Service
public class UserService {
@Cacheable("users")
public User findById(Long id) {
// DB에서 사용자 조회
return userRepository.findById(id);
}
@CacheEvict("users")
public void updateUser(User user) {
// 사용자 업데이트 후 캐시 제거
}
}
웹소켓
실시간 양방향 통신을 가능하게 하는 프로토콜입니다.
특징
- Full-duplex 통신
- HTTP와 호환
- 낮은 오버헤드
- 실시간 데이터 전송
Spring WebSocket 설정
java@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(new MyWebSocketHandler(), "/websocket");
}
}
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(new MyWebSocketHandler(), "/websocket");
}
}
사용 사례
- 채팅 애플리케이션
- 실시간 알림
- 온라인 게임
- 주식 거래 시스템
Spring Security
Spring 기반 애플리케이션의 보안을 담당하는 프레임워크입니다.
핵심 기능
- 인증 (Authentication): 사용자가 누구인지 확인
- 인가 (Authorization): 사용자의 접근 권한 확인
- 보안 취약점 방어: CSRF, XSS, SQL Injection 등
주요 구성 요소
- SecurityFilterChain: 보안 필터들의 체인
- AuthenticationManager: 인증 처리 매니저
- UserDetailsService: 사용자 정보 로드
- PasswordEncoder: 비밀번호 암호화
설정 예시
java@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
return http
.authorizeHttpRequests(auth -> auth
.requestMatchers("/public/**").permitAll()
.anyRequest().authenticated())
.formLogin(withDefaults())
.build();
}
}
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
return http
.authorizeHttpRequests(auth -> auth
.requestMatchers("/public/**").permitAll()
.anyRequest().authenticated())
.formLogin(withDefaults())
.build();
}
}
Java 동기화
멀티스레드 환경에서 공유 자원에 대한 안전한 접근을 보장하는 메커니즘입니다.
synchronized 키워드
javapublic class Counter {
private int count = 0;
public synchronized void increment() {
count++;
}
public synchronized int getCount() {
return count;
}
}
public class Counter {
private int count = 0;
public synchronized void increment() {
count++;
}
public synchronized int getCount() {
return count;
}
}
java.util.concurrent 패키지
- AtomicInteger: 원자적 연산 제공
- ConcurrentHashMap: 동시성을 지원하는 HashMap
- CountDownLatch: 스레드 동기화 유틸리티
Lock 인터페이스
javaReentrantLock lock = new ReentrantLock();
public void method() {
lock.lock();
try {
// 임계 영역
} finally {
lock.unlock();
}
}
ReentrantLock lock = new ReentrantLock();
public void method() {
lock.lock();
try {
// 임계 영역
} finally {
lock.unlock();
}
}
비동기 처리
작업을 병렬로 처리하여 성능을 향상시키는 프로그래밍 패러다임입니다.
Spring @Async
java@Service
public class AsyncService {
@Async
public CompletableFuture<String> processAsync() {
// 비동기 작업 수행
return CompletableFuture.completedFuture("결과");
}
}
@Service
public class AsyncService {
@Async
public CompletableFuture<String> processAsync() {
// 비동기 작업 수행
return CompletableFuture.completedFuture("결과");
}
}
CompletableFuture
javaCompletableFuture<String> future = CompletableFuture
.supplyAsync(() -> "Hello")
.thenApply(s -> s + " World")
.thenCompose(s -> CompletableFuture.supplyAsync(() -> s + "!"));
CompletableFuture<String> future = CompletableFuture
.supplyAsync(() -> "Hello")
.thenApply(s -> s + " World")
.thenCompose(s -> CompletableFuture.supplyAsync(() -> s + "!"));
비동기 처리 방식
- 콜백 (Callback): 작업 완료 시 호출되는 함수
- Future/Promise: 미래의 결과를 나타내는 객체
- Reactive Streams: 데이터 스트림의 비동기 처리
람다 (Lambda)
Java 8에서 도입된 함수형 프로그래밍 기능입니다.
문법
java// 기본 문법
(parameters) -> expression
(parameters) -> { statements; }
// 예시
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
names.stream()
.filter(name -> name.length() > 3)
.map(name -> name.toUpperCase())
.forEach(System.out::println);
// 기본 문법
(parameters) -> expression
(parameters) -> { statements; }
// 예시
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
names.stream()
.filter(name -> name.length() > 3)
.map(name -> name.toUpperCase())
.forEach(System.out::println);
함수형 인터페이스
- Predicate<T>: 조건 검사
- Function<T, R>: 변환 작업
- Consumer<T>: 소비 작업
- Supplier<T>: 공급 작업
메소드 참조
java// 람다 표현식
names.forEach(name -> System.out.println(name));
// 메소드 참조
names.forEach(System.out::println);
// 람다 표현식
names.forEach(name -> System.out.println(name));
// 메소드 참조
names.forEach(System.out::println);
영속성 컨텍스트 (Persistence Context)
JPA에서 엔티티를 관리하는 환경으로, 엔티티의 생명주기를 관리합니다.
엔티티 생명주기
- 비영속 (New/Transient): 영속성 컨텍스트와 관계없는 상태
- 영속 (Managed): 영속성 컨텍스트에 저장된 상태
- 준영속 (Detached): 영속성 컨텍스트에서 분리된 상태
- 삭제 (Removed): 삭제하기로 결정된 상태
1차 캐시
영속성 컨텍스트 내부에 있는 캐시로, 같은 식별자의 엔티티는 같은 인스턴스를 보장합니다.
변경 감지 (Dirty Checking)
java@Transactional
public void updateUser(Long id, String name) {
User user = userRepository.findById(id);
user.setName(name); // 자동으로 UPDATE 쿼리 실행
}
@Transactional
public void updateUser(Long id, String name) {
User user = userRepository.findById(id);
user.setName(name); // 자동으로 UPDATE 쿼리 실행
}
지연 로딩 (Lazy Loading)
java@Entity
public class User {
@OneToMany(fetch = FetchType.LAZY)
private List<Order> orders;
}
@Entity
public class User {
@OneToMany(fetch = FetchType.LAZY)
private List<Order> orders;
}
String, StringBuilder, StringBuffer
Java에서 문자열을 다루는 세 가지 클래스입니다.
String
- 불변 (Immutable) 객체
- 문자열 변경 시 새로운 객체 생성
- 메모리 비효율적 (많은 연산 시)
javaString str = "Hello";
str += " World"; // 새로운 String 객체 생성
String str = "Hello";
str += " World"; // 새로운 String 객체 생성
StringBuilder
- 가변 (Mutable) 객체
- 스레드 안전하지 않음
- 단일 스레드 환경에서 높은 성능
javaStringBuilder sb = new StringBuilder();
sb.append("Hello").append(" World");
String result = sb.toString();
StringBuilder sb = new StringBuilder();
sb.append("Hello").append(" World");
String result = sb.toString();
StringBuffer
- 가변 (Mutable) 객체
- 스레드 안전함 (synchronized)
- 멀티스레드 환경에서 안전하지만 성능 저하
성능 비교
- 적은 연산: String > StringBuilder > StringBuffer
- 많은 연산: StringBuilder ≈ StringBuffer >> String
AOP (Aspect-Oriented Programming)
관점 지향 프로그래밍으로, 횡단 관심사를 모듈화하는 프로그래밍 패러다임입니다.
핵심 개념
- Aspect: 횡단 관심사를 모듈화한 것
- Join Point: Advice가 적용될 수 있는 위치
- Pointcut: Join Point를 선별하는 표현식
- Advice: 실제로 실행되는 코드
- Weaving: Advice를 핵심 로직에 적용하는 과정
Advice 종류
- @Before: 메소드 실행 전
- @After: 메소드 실행 후
- @AfterReturning: 정상 반환 후
- @AfterThrowing: 예외 발생 후
- @Around: 메소드 실행 전후
예시
java@Aspect
@Component
public class LoggingAspect {
@Around("@annotation(Loggable)")
public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
long start = System.currentTimeMillis();
Object result = joinPoint.proceed();
long end = System.currentTimeMillis();
System.out.println("실행 시간: " + (end - start) + "ms");
return result;
}
}
@Aspect
@Component
public class LoggingAspect {
@Around("@annotation(Loggable)")
public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
long start = System.currentTimeMillis();
Object result = joinPoint.proceed();
long end = System.currentTimeMillis();
System.out.println("실행 시간: " + (end - start) + "ms");
return result;
}
}
로깅 (Logging)
애플리케이션의 실행 상태와 오류 정보를 기록하는 과정입니다.
로그 레벨
- TRACE: 가장 상세한 정보
- DEBUG: 디버깅용 정보
- INFO: 일반 정보
- WARN: 경고 메시지
- ERROR: 오류 메시지
- FATAL: 치명적 오류
SLF4J + Logback
javaimport org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Service
public class UserService {
private static final Logger logger = LoggerFactory.getLogger(UserService.class);
public User findUser(Long id) {
logger.debug("사용자 조회 시작: {}", id);
try {
User user = userRepository.findById(id);
logger.info("사용자 조회 완료: {}", user.getName());
return user;
} catch (Exception e) {
logger.error("사용자 조회 실패: {}", id, e);
throw e;
}
}
}
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Service
public class UserService {
private static final Logger logger = LoggerFactory.getLogger(UserService.class);
public User findUser(Long id) {
logger.debug("사용자 조회 시작: {}", id);
try {
User user = userRepository.findById(id);
logger.info("사용자 조회 완료: {}", user.getName());
return user;
} catch (Exception e) {
logger.error("사용자 조회 실패: {}", id, e);
throw e;
}
}
}
Logback 설정 (logback-spring.xml)
xml<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<logger name="com.example" level="DEBUG"/>
<root level="INFO">
<appender-ref ref="STDOUT"/>
</root>
</configuration>
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<logger name="com.example" level="DEBUG"/>
<root level="INFO">
<appender-ref ref="STDOUT"/>
</root>
</configuration>
DTO, DAO, VO
데이터를 다루는 객체들의 설계 패턴입니다.
DTO (Data Transfer Object)
계층 간 데이터 전송을 위한 객체로, 로직을 포함하지 않고 데이터만 전송합니다.
public class UserDto {
private Long id;
private String name;
private String email;
// 생성자, getter, setter
public UserDto(Long id, String name, String email) {
this.id = id;
this.name = name;
this.email = email;
}
}
DAO (Data Access Object)
데이터베이스에 접근하는 로직을 캡슐화한 객체입니다.
@Repository
public class UserDao {
private final JdbcTemplate jdbcTemplate;
public User findById(Long id) {
String sql = "SELECT * FROM users WHERE id = ?";
return jdbcTemplate.queryForObject(sql, new UserRowMapper(), id);
}
public void save(User user) {
String sql = "INSERT INTO users (name, email) VALUES (?, ?)";
jdbcTemplate.update(sql, user.getName(), user.getEmail());
}
}
VO (Value Object)
값을 표현하는 불변 객체로, 동등성을 값으로 판단합니다.
public class Money {
private final int amount;
private final String currency;
public Money(int amount, String currency) {
this.amount = amount;
this.currency = currency;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
Money money = (Money) obj;
return amount == money.amount && Objects.equals(currency, money.currency);
}
@Override
public int hashCode() {
return Objects.hash(amount, currency);
}
}