1. 해시 vs 암호화
- 해시(Hash)
- 입력(메시지)을 고정 길이의 출력(해시값)으로 변환. 같은 입력은 항상 같은 출력, 아주 작은 입력 변경도 출력 크게 변화.
- 단방향 함수: 원래 메시지로 복원할 수 없음(복호화 불가).
- 용도: 비밀번호 저장(솔트와 함께), 무결성 검사(파일 무결성, 서명 전 해시), 해시 키(해시맵, 캐싱) 등.
- 주의: 단순 해시는 충돌(다른 입력이 같은 해시값)이 가능하므로 민감정보(비밀번호)는 반드시 솔트+비밀번호 해싱 알고리즘 사용.
- 암호화(Encryption)
- 데이터를 다른 형태로 변환하여 복원 가능하게 만드는 과정. 복호화(Decrypt) 가능.
- 대칭키 암호화(AES 등): 암/복호화에 같은 키 사용. 빠르고 대용량 데이터 암호화에 적합.
- 비대칭키 암호화(RSA 등): 공개키(Public Key)로 암호화, 개인키(Private Key)로 복호화 — 키 분배 문제를 해결하고, 디지털 서명에 사용.
2. 주요 알고리즘 원리
SHA (SHA-1, SHA-2, SHA-3)
- 분류: 해시 함수(단방향).
- 특징:
- SHA-1은 취약(충돌 가능) → 사용 권장하지 않음.
- SHA-2(SHA-256 등), SHA-3는 현재 널리 사용.
- 용도: 데이터 무결성, 디지털 서명의 메시지 다이제스트, HMAC 등.
- 주의: 비밀번호 저장에는 단순 SHA-256만 사용하지 말 것 — 반드시 PBKDF2 / bcrypt / scrypt / Argon2 같은 키 스트레칭 알고리즘 + 솔트 사용.
AES (Advanced Encryption Standard)
- 분류: 대칭키 블록 암호. (주로 AES-128/192/256)
- 원리(요약): 고정 블록(128비트)을 여러 라운드로 치환·섞기(서브바이트, 시프트행, 믹스컬럼 등).
- 운영모드: ECB(비권장), CBC, GCM(인증 암호화: 암호화 + 무결성) 등.
- 실무 팁: 데이터 암호화 시 GCM 같은 인증 모드를 써서 무결성(탬퍼 여부)도 확보. 키 관리(KMS)를 꼭 고려.
RSA (Rivest–Shamir–Adleman)
- 분류: 비대칭 암호화(공개키/개인키)
- 원리(요약): 큰 소수의 곱과 모듈러 산술의 어려움을 기반으로 함. 공개키로 암호화 → 개인키로 복호화. 또한 개인키로 서명 → 공개키로 검증.
- 용도: 소규모 데이터 암호화(실무에서는 대칭 키(예: AES 키)를 RSA로 암호화 해서 전송), 디지털 서명, TLS 핸드쉐이크(대칭키 교환) 등.
- 실무 팁: RSA는 큰 데이터 암호화에 비효율적(성능/사이즈). 최신 시스템은 RSA 대신 ECDSA/ECDH(타원곡선 알고리즘)를 사용하기도 함.
3. 인증(Authentication) vs 인가(Authorization)
- 인증(Authentication): 사용자가 누구인지 확인하는 과정(로그인).
- 인가(Authorization): 인증된 사용자가 특정 리소스/행위를 할 권한이 있는지 판단(권한 체크).
둘은 서로 보완적이며 보안 설계에서 둘 모두 올바르게 구현되어야 합니다.
세션(Session)과 쿠키(Cookie)
- 쿠키: 클라이언트(브라우저)에 저장되는 작은 키-값. 세션 ID, 토큰 등을 저장하는 데 사용.
- 설정 고려사항:
HttpOnly, Secure, SameSite 속성으로 XSS/CSRF 완화.
- 세션: 서버가 사용자 상태(로그인 상태 등)를 관리하는 방법. 흔히 세션 ID를 쿠키로 클라이언트에 넣고, 서버는 세션 스토어(Redis 등)에 상태를 저장.
- 장점: 토큰 탈취 시 서버에서 바로 무효화 가능.
- 단점: 서버 상태(스케일링/세션 동기화) 부담.
예시 흐름 (쿠키+세션)
- 사용자 로그인 → 서버는 세션 생성(서버 메모리/Redis에 저장), 세션 ID를 쿠키로 발급.
- 브라우저는 이후 요청에 쿠키 포함. 서버는 세션 ID로 세션을 조회해 인증 확인.
JWT (JSON Web Token)
- 구성:
header.payload.signature (Base64Url 인코딩)
- 특징:
- 자체 포함(self-contained): 토큰 안에 사용자 정보(클레임)를 포함 → 서버에서 별도 세션 저장소 없이 인증 가능.
- 서명(signature)을 검증해서 변조 여부 확인. 서명은 대칭(HS256) 또는 비대칭(RS256) 방식 사용.
- 토큰 자체에 만료(exp) 클레임 포함 가능.
- 장점: 무상태(stateless) API에 적합, 확장성 좋음(서버 간 세션 동기화 필요 없음).
- 단점 & 주의:
- 토큰 탈취 시 만료까지 유효 → 리프레시 토큰, 짧은 액세스 토큰 만료 시간, 토큰 블랙리스트(또는 토큰 버전링)로 대처 필요.
- 토큰 재사용 공격, XSS 취약점에 의한 토큰 탈취 위험 —
HttpOnly 쿠키로 저장하거나 스토리지 사용 시 주의 필요.
- 중요한 권한 변경 시(예: 관리자 권한 회수) 서버에서 즉시 반영하려면 추가 관리 필요.
JWT 발급/검증 (간단 예시, Node.js 스타일 - 의사코드)
const payload = { sub: userId, role: 'user', iat: now, exp: now + 15*60 };
const token = jwt.sign(payload, SECRET_KEY, { algorithm: 'HS256' });
try {
const payload = jwt.verify(token, SECRET_KEY);
req.user = payload;
} catch (err) {
}
OAuth2 (및 OpenID Connect)
- 목적: 제3자(클라이언트)가 리소스 소유자(사용자)의 자원에 접근하도록 “권한”(Authorization)을 부여하게 해주는 표준.
- 흐름(대표적):
- Authorization Code Grant: 서버사이드 앱에서 안전하게 사용. 클라이언트가 인증서버(예: Google)로부터 authorization code를 받고, 서버가 이를 토큰으로 교환.
- Implicit, Resource Owner Password, Client Credentials 등 여러 흐름이 있으나 보안성에 따라 선호도가 다름.
- OpenID Connect (OIDC): OAuth2 위에 인증(Authentication)을 위한 표준을 덧붙임(아이디 토큰 ID Token 제공).
- 실무 포인트:
- 외부 로그인(구글, 카카오 등)은 OAuth2/OIDC 사용.
- 브라우저 앱(싱글 페이지 앱)은 Authorization Code + PKCE(Proof Key for Code Exchange)를 권장.
4. 시스템 설계 예시: URL Shortener
요구사항(요약)
- 기능: 긴 URL을 짧게 변환, 원래 URL로 리다이렉트
- 비기능: 초당 트래픽(예: 1000 RPS) 대응, 충돌 없음(또는 충돌 최소화), 통계(클릭 수), 단축 URL 예측 방지
기본 컴포넌트
- API 서버 (POST /shorten, GET /{short})
- 데이터베이스: RDB(MySQL) 또는 NoSQL(Cassandra)
- 캐시: Redis (빠른 리다이렉션, 통계 집계)
- 단축키 생성 서비스: 해시/베이스 인코딩 또는 고유 ID 기반
데이터 모델 (간단)
Table urls (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
short_key VARCHAR(10) UNIQUE,
original_url TEXT,
created_at TIMESTAMP,
expire_at TIMESTAMP NULL,
owner_id BIGINT NULL
);
Table clicks (
id BIGINT AUTO_INCREMENT,
url_id BIGINT,
created_at TIMESTAMP,
ip VARCHAR(45),
user_agent TEXT
);
short_key 생성 전략
- Auto-increment ID -> Base62 인코딩
- 장점: 간단, 충돌 없음.
- 단점: 예측 가능(쉽게 유추 가능). 해결: ID에 random salt/offset 적용 또는 다른 매핑 사용.
- 랜덤 문자열(충돌 체크)
- 장점: 예측 어려움.
- 단점: 충돌 가능성(짧을수록 충돌 확률↑) → 충돌 시 재시도.
- Hash(original_url) 일부 사용 + 충돌 해결
- 충돌을 DB에서 검사하고 충돌 시 다른 방식 적용.
API 예시
POST /shorten 요청: { "url": "https://example.com/long/...", "expire_in_days": 30 } 응답: { "short_url": "https://s.example/abc123" }
GET /{short_key} 동작: 캐시 -> DB 조회 -> 클릭 카운트(비동기 처리 가능) -> 301 리다이렉트
성능·확장 전략
- 캐시 우선: 자주 요청되는 short_key는 Redis에 원본 URL 캐시.
- CDN/엣지 리다이렉트: 초고속 리다이렉트를 위해 엣지에 단축키 매핑을 배포 가능.
- 통계 집계: 클릭 로깅은 메시지 큐(Kafka)로 비동기 전송 후 배치 처리로 집계.
- 데이터 파티셔닝: DB 쓰기 병목 시 파티셔닝 또는 샤딩 적용.
보안 고려사항
- 악성 URL(피싱, 악성코드) 필터링(체크 서비스 연동)
- API rate limiting(브루트포스/대량 단축 방지)
- short_key 예측 방지: 충분히 랜덤하고 길이 설정
- 개인 정보: 원본 URL에 민감 정보 포함 시 저장/보관 정책
5. 시스템 설계 예시: 채팅 시스템 (실시간 메시징)
요구사항(요약)
- 1:1 채팅, 그룹 채팅 지원
- 실시간 전달(근실시간), 메시지 영속화(대화 기록), 읽음/전달 상태, 오프라인 푸시, 확장성
주요 컴포넌트
- 클라이언트: 웹(브라우저) — WebSocket, 모바일 앱 — WebSocket or native socket
- API 서버: 인증, 대화방 관리, 메시지 저장
- Message Broker / Pub-Sub: Redis Pub/Sub, Kafka, RabbitMQ 등 (서버 간 메시지 분배)
- WebSocket Gateway: 연결 유지, 메시지 라우팅(사용자 연결 정보 매핑)
- DB: 메시지 저장(예: PostgreSQL, Cassandra)
- Presence Service: 누가 온라인인지 관리(예: Redis)
- Push Service: APNs/FCM 연동 (오프라인 사용자에게 푸시)
메시지 흐름(요약)
- 클라이언트 ↔ WebSocket 연결(토큰으로 인증)
- A가 메시지 전송 → WebSocket Gateway 수신 → 메시지 검증/저장 → Broker로 게시
- Broker가 해당 채팅을 구독 중인 접속 서버들에 메시지 배포
- 구독 서버는 연결된 클라이언트에게 메시지 푸시(또는 오프라인이면 Push Service로 푸시)
- 읽음/전달 상태는 별도 이벤트로 처리
데이터 모델(간단)
messages (
id BIGINT,
room_id BIGINT,
sender_id BIGINT,
content TEXT,
type ENUM('text','image','system'),
created_at TIMESTAMP,
delivered BOOL DEFAULT FALSE,
read_at TIMESTAMP NULL
);
rooms (id, name, type, created_at);
room_members (room_id, user_id, joined_at);
확장성 전략
- Stateless 서버 + 외부 스토어: WebSocket 연결 메타데이터(유저→서버 매핑)는 Redis에 저장.
- 샤딩: 사용자 기반으로 WebSocket 게이트웨이 샤딩.
- 메시지 파티셔닝: 대량 메시지 처리 시 Kafka + 여러 컨슈머 그룹으로 확장.
- 미들웨어: 메시지 필터(악성 콘텐츠), 사이즈 제한, 파일 업로드 처리(별도 스토리지 S3).
일관성/전달 보장
- At-most-once: 단순, 중복 허용.
- At-least-once: 중복 가능성 있으나 재시도 로직 필요(메시지 IDempotency).
- Exactly-once: 복잡(트랜잭션 등). 대부분 채팅은 at-least-once + idempotency 처리로 충분.
보안 고려사항
- 인증된 연결만 허용 (JWT/세션 토큰으로 WebSocket 인증)
- 메시지 암호화: 전송 계층 TLS 사용. 민감한 대화의 경우 E2E 암호화(클라이언트 측 암호화) 고려.
- 권한 체크: 방 입장 권한, 메시지 삭제 권한 등 서버에서 강제 검증.
6. 실무에서 자주 하는 보안/설계 체크리스트
- 비밀번호: 솔트 + 적절한 해시 함수(PBKDF2/bcrypt/Argon2) 사용
- 전송: TLS(HTTPS) 강제 적용
- 토큰 관리: 액세스 토큰 짧은 만료, 리프레시 토큰 안전 보관 및 관리
- 세션 보안:
HttpOnly, Secure, SameSite 쿠키 설정
- 입력 검증: XSS/SQL Injection 방지(PreparedStatement, ORM 사용), 출력 이스케이핑
- 로깅/모니터링: 보안 로그(로그인 실패, 토큰 재발급 등) 수집 및 이상 징후 탐지
- 키 관리: 암호화 키는 별도 KMS(예: AWS KMS)로 관리
- 테스팅: 정기적인 취약점 스캔, 펜테스트
실무에서 적용하는 체크리스트가 있어 이를 토대로 앞으로 개발할 때 더 신경써서 개발할 수 있을 거 같아요!