블로그
개발 관련 지식 및 개발기를 주로 다룹니다.
Next.js 프로젝트에서 i18n으로 다국어 사이트를 만들어보자
Next.js에서 i18n을 적용해보고 간단하게 정리해보자. | 현재 사이트를 한국 사용자뿐만 아니라 글로벌 사용자에게도 지원되게끔 해보고 싶었다. (볼 사람이 있을까 싶지만, 그냥 한 번 해보고 싶었다.) 이를 위해 웹사이트에 다국어 지원, 즉 국제화(Internationalization, i18n)를 도입하기로 했다. 이 사이트는 Next.js 프로젝트였기에 널리 사용되는 라이브러리인 next-i18next를 선택했다. i18n 및 next-i18next의 특징 Next.js와의 통합성: next-i18next 라이브러리를 통해 Next.js의 서버 사이드 렌더링(SSR), 정적 사이트 생성(SSG)과도 잘 호환된다. 사용 편의성: 러닝 커브가 낮고, 기존 코드에 쉽게 적용할 수 있다. 기능성: 로케일 기반 라우팅, 번역 파일 관리, 복수형 처리 등 다양한 기능을 지원한다. Next.js(Page Router) 프로젝트에 next-i18next를 사용해 i18n을 적용하는 과정을 간단하게 정리해보았다. 라이브러리 설치하기 먼저 next
TCP의 연결 수립/종료 과정에 대해 알아보자
TCP의 연결 수립/종료 과정에 대해 알아보자 | TCP (Transmission Control Protocol) 연결 기반 (3-way handshake 사용) 신뢰성 보장: 순서 보장, 패킷 유실 시 재전송 흐름 제어, 혼잡 제어 포함 예시: HTTP, HTTPS, FTP, 이메일 UDP (User Datagram Protocol) 비연결형: handshake 없음 비신뢰성: 순서/유실 보장 없음 속도 빠름, 오버헤드 적음 예시: DNS, VoIP, 게임, 실시간 스트리밍 TCP vs UDP 비교 TCP 연결 수립 (3-way Handshake) SYN (Synchronize) 클라이언트가 초기 시퀀스 번호와 함께 서버에 연결 요청 전송 SYN-ACK (Synchronize-Acknowledge) 서버가 클라이언트의 SYN을 수신한 뒤, 자신의 ISN을 클라이언트의 ISN + 1을 ACK 번호로 하여 SYN+ACK 응답 전송 ACK (Acknowledge) 클라이언트가 서버의 응답을 받고 다시 서버의 ISN
꼭 필요하지만 가끔은 귀찮은 CORS 정리
보안을 위해 꼭 필요한 기능이지만, 가끔은 너무나 귀찮게 느껴지는 CORS를 한 번 정리해보자. | CORS (Cross-Origin Resource Sharing) 대부분의 개발자는 외부 API를 연동하면서 CORS 문제를 한 번쯤 겪어봤을 것이다. CORS는 브라우저가 다른 출처(origin)의 자원에 접근할 때 보안을 위해 서버에 요청 권한을 묻는 정책이다. 동일 출처가 아닌 요청은 기본적으로 차단된다. 동일 출처(origin)란? 동일 출처란 아래 세 가지가 모두 같아야 한다: 프로토콜 (http, https) 도메인 (정민.xyz, google.com) 포트 (80, 443 등) https://정민.xyz 기준 예시: CORS 동작 방식 클라이언트에서 다른 origin으로 요청을 보낸다. 브라우저는 Preflight 요청을 먼저 보낸다. OPTIONS 메서드로 서버에 “이 요청 허용되나요?”를 묻는다. 서버는 Access-Control-Allow-* 헤더를 포함하여 응답한다. 브라우저가 허용되었다고 판단하면 실제 요청을 진행한다. 모든 요청이 Preflight 요청을
Next.js + PWA를 이용해 웹에서 앱 푸쉬를 보내보자
Next.js(Page Router)에 PWA를 적용하는 과정을 정리해보자. | 최근 사이드 프로젝트를 진행하며 웹 기반 서비스지만 앱처럼 쉽게 접근하고, 필요할 때 사용자에게 알림을 보내는 기능이 필요했다. 웹과 앱의 장점을 최대한 쉽게 취하기 위해 여러 기술 스택을 찾아봤다. Flutter React Native + Capacitor Native + (KMP): 네이티브로 각각 필요한 기능을 구현하고, 공통 로직들은 KMP로 분리 가장 성능이 뛰어나지만, 플랫폼별 개발과 배포 관리에 부담이 있었다. PWA (Progressive Web App): 웹 기술만으로 앱과 유사한 경험을 제공할 수 있었다. 결론적으로 가장 친숙한 기술로 요구사항을 충족할 수 있는 PWA를 선택했다. 이 글은 Next.js(Page Router) 프로젝트에 PWA를 구현하며 겪은 과정을 정리한 기록이다. manifest.json 정의 PWA의 첫 단계는 웹사이트가 앱처럼 설치 가능하다는 것을 브라우저에게 알려주는 것이다. 이 역할을 하는 것이 바로 public/manifest.
프로세스와 스레드의 겉을 핥아보자
프로세스와 스레드, 그리고 시스템 콜에 대해 간단히 알아보자 | 프로세스와 스레드 프로세스 운영체제에서 실행 중인 프로그램을 의미하며, 독립된 실행 환경을 갖추고 있다. 프로세스는 운영체제에서 자원을 할당받고, 자신만의 주소 공간을 가진다. 이 주소 공간은 크게 코드(실행 파일), 데이터(초기화된 전역 변수 등), 힙(동적 메모리 할당 공간), 스택(함수 호출, 지역 변수 등) 영역으로 나뉜다. 스레드 프로세스 내에서 실행되는 독립적인 흐름으로, 하나의 프로세스 내에서 여러 스레드가 실행될 수 있다. 스레드는 프로세스의 코드, 데이터, 힙 영역을 공유하지만, 각 스레드는 자기만의 스택을 가진다. 즉, 스레드는 메모리 공간을 일부 공유하고 일부는 독립적으로 사용한다. 따라서 하나의 프로세스 내에서 여러 스레드를 생성해 병렬 혹은 비동기 처리를 할 수 있다. 스레드 간의 동시성(concurrency)을 관리하는 것이 중요하며, 여러 스레드가 공유하는 자원을 안전하게 다루기 위해 동기화가 필요하다. 예를 들어, 동시에 여러 스레드가 동일한 변수에 접근
자바의 가비지 콜렉터는 어떻게 돌아갈까?
자바의 가비지 콜렉터(Garbage Collector)에 대해 간단히 알아보자 | GC(Garbage Collection란 무엇인가? 자바에서 객체는 new 키워드로 생성되며 힙(heap) 메모리 공간에 저장된다. 그런데 사용하지 않는 객체들이 계속 쌓인다면 메모리가 부족해질 것이다. 그래서 자바는 Garabage Collecting(쓰레기 수집)이라는 자동 메모리 관리 기능을 통해 더 이상 필요 없는 객체를 찾아내어 메모리에서 제거한다. GC의 기본 구조 (객체 생존 주기에 따른 Generational GC) 자바의 GC는 객체의 생존 주기에 따라 메모리를 나누고, 각 영역별로 최적화된 방식을 적용한다. 이것이 바로 Generational GC(세대별 GC)이다. 세대별 메모리 영역 Young Generation (젊은 세대): 새로 생성된 객체들이 저장된다. 대부분 여기서 소멸한다. Old Generation (늙은 세대): 여러 차례 GC를 거쳐 살아남은 장기 객체가 저장된다. ### Young Generation 내부 구조: Eden과 Survivor
HTTP/HTTPS에 대해 다시 한 번 정리해보자
잊혀지기 직전에 다시 꺼내보는 HTTP와 HTTPS | HTTP HyperText Transfer Protocol의 약자로 웹에서 클라이언트(주로 브라우저)와 서버가 통신할 때 사용하는 프로토콜 HTTPS HyperText Transfer Protocol Secure의 약자로 HTTP에 SSL/TLS를 추가해 데이터를 암호화하여 안전하게 전송하는 프로토콜 대칭키 + 공개키 암호화 사용 공개키로 대칭키를 암호화 → 서버가 비공개키로 복호화 보안 요소 기밀성: 암호화로 제3자가 내용을 볼 수 없음 무결성: 데이터가 변경되지 않았는지 확인 (해시) 인증: 서버가 진짜인지 확인 (인증서 기반) HTTPS 통신 주요 과정 `plaintext 클라이언트 → 서버로 접속 요청 (HTTPS) 서버 → 인증서(CA 발급) 전달 클라이언트 → 인증서 검증 대칭키 생성 후 서버에 전달 (공개키로 암호화) 서버 → 비공개키로 복호화 이후 대칭키로 통신 ` HTTP 핵심 특징 비연결성 (Connectionless) 클라이언트가 요청하고 서버가 응답을 보내면,
DNS - Google.com에 접속하면 무슨 일이 일어날까?
DNS를 정말 간단하게 정리해보자 | DNS Domain Name System 도메인 이름(예: google.com)을 IP 주소로 변환해주는 시스템 작동 과정 사용자가 브라우저에 도메인 입력 로컬 캐시 확인 → 없으면 DNS 서버로 쿼리 루트 DNS → TLD DNS → 권한 있는 DNS 서버 차례로 탐색 최종 IP 주소 응답 IP 주소로 실제 서버에 접속 캐싱 단계 브라우저 캐시 (Browser Cache) 위치: 웹 브라우저 내부 (Chrome, Firefox 등) 내용: 최근 방문한 도메인의 DNS 응답(IP 주소, TTL) TTL(유효기간): 네임서버가 제공한 TTL 값에 따라 결정 (몇 초 ~ 수 시간) 특징 가장 먼저 확인하는 캐시 같은 브라우저 탭/창에서 같은 사이트 재방문 시 매우 빠름 브라우저 종료 시 캐시가 일부/전체 삭제될 수 있음 예시 google.com → 142.250.206.78 (남은 TTL 200초) 운영체제(OS) 캐시 위치: OS의 DNS Resolver Windows: DNS
MySQL 쿼리 실행 구조를 간단하게 알아보자
MySQL의 구조 및 실행 과정 간단 정리 | MySQL 구조 Query Parser (쿼리 파서) 사용자가 보낸 SQL 쿼리를 구문 분석 SQL 문장이 문법적으로 올바른지 검사 (예: SELECT, FROM 등의 키워드 확인) 파싱된 쿼리는 내부 트리(파스 트리) 형태로 변환됨 Preprocessor (프리프로세서) 파스 트리를 기반으로 쿼리 내 객체(테이블, 칼럼 등)의 존재 여부 확인 접근 권한 검사 이름 해석(name resolution) 수행 예외: 정의되지 않은 테이블이나 컬럼이 있을 경우 오류 발생 Optimizer (옵티마이저) 쿼리 실행 계획 수립 (어떤 인덱스를 사용할지, 조인 순서는 어떤지 등 결정) 비용 기반(cost-based) 옵티마이저 또는 규칙 기반(rule-based) 사용 가능한 여러 실행 계획 중 가장 효율적인 것을 선택 Execution Engine (실행 엔진) 옵티마이저에서 선택된 실행 계획을 따라 실제 쿼리를 수행 스토리지 엔진과 통신하며 데이터 접근 중간 결과를 메모리에서 처리하며 결과
Aligo API를 이용한 Kotlin Spring 기반의 SMS/알림톡 라이브러리
Aligo를 이용해 SMS/알림톡 라이브러리를 만들고 이를 서비스에 연동하는 과정을 알아보자 | Aligo API를 이용해 Kotlin과 Spring Framework 기반의 SMS/알림톡 라이브러리를 구축하고 서비스에 연동한 경험을 공유해보고자 한다. 왜 만들었나? 다양한 프로젝트에서 SMS, 알림톡 등 메시지 발송은 흔한 요구사항이다. 여러 서비스에서 항공권 발권, 예약 변경 안내, 마케팅 등 다양한 목적으로 사용자에게 문자나 카카오 알림톡을 보내야 했다. 서비스에서 직접 Aligo API를 호출하고 데이터를 파싱하는 방식은 Aligo 서비스 자체에 종속되어 추후 API 명세가 변경되거나, 다른 서비스로 변경할 때 유연하게 대응하기 어려울 것이라 생각했다. 이러한 문제를 해결하기 위해 SMS/알림톡 전송을 위한 별도 라이브러리를 만들게 되었다. 프로젝트 목표 추상화: HTTP 요청, 인증 처리, JSON 직렬화 등 상세 과정은 숨기고, 알림 전송에 필요한 기능들만 추상화하여 노출한다. 느슨한 결합: ISendMessageUseCase 인터페이스를 통해 실제 구현체는 분리
WebSocket과 Kafka를 이용해 실시간 알림 서비스를 만들어보자
WebSocket과 Kafka를 활용한 실시간 알림 서비스 구축기 | 서비스에서 발생하는 이벤트를 사용자에게 실시간으로 전달하기 위해, Kotlin, Spring Boot, Kafka, WebSocket을 기반으로 알림 전송 서비스를 구축한 경험을 공유해보자. 왜 만들었나? 항공 서비스 백오피스를 개발하면서 항공편 상태 변경, 신규 예약, 내부 공지 등 다양한 이벤트를 특정 사용자 그룹에게 실시간으로 전달해야 하는 요구사항이 있었다. 해당 프로젝트 내에서 WebSocket 연결을 관리하고 알림 로직을 작성하는 건 부적절하다 생각해 알림만을 위한 별도 서비스를 만들었다. 당시에는 MSA 환경이 아니었고 대용량 트래픽도 없었다. 다만 Kafka는 서버 장애 시에도 메시지 유실 없이 안정적이며 대용량 알림 전달에도 용이하다. 또한 혹여나 미래에 MSA 전환 시 별도 작업 없이 시스템을 그대로 활용할 수 있기에 Kafka를 써보았다. 프로젝트 목표 디커플링(Decoupling): 알림을 생성하는 서비스와 알림을 전송하는 시스템을 완전히 분리한
AWS SES와 Kotlin/Gradle로 메일 발송 라이브러리 만들기
AWS SES SDK를 이용해 Kotlin/Gradle 기반의 간단한 메일 발송 라이브러리를 만드는 과정을 정리해보았다. | AWS의 이메일 서비스인 SES(Simple Email Service)를 활용해서, Kotlin과 Gradle 기반의 간단한 메일 발송 라이브러리를 구축한 경험을 공유한다. 왜 만들었나? 항공 서비스를 개발하면서 회원가입을 위한 초대 메일 발송, 인증 등 다양한 목적으로 이메일을 보내야 했다. 특히 많은 사용자에게 개인화된 메일을 보내려면 안정적이고 확장 가능한 시스템이 필수다. AWS SES는 저렴한 비용에 높은 전송률을 보장하는 좋은 솔루션이고, 이걸 좀 더 편하게 회사의 주요 용도에 맞게 재사용하려고 직접 기능들을 래핑했다. 프로젝트 목표 재사용성: 어떤 Kotlin/Java 프로젝트든 쉽게 의존성을 추가해서 쓸 수 있는 모듈화된 라이브러리를 만든다. 추상화: SES의 복잡한 설정을 감추고, 개발자가 최소한의 정보(수신자, 제목 등)만으로 메일을 보내게 간단한 인터페이스를 제공한다. 프로젝트 구조 표준 Gradle/Kotlin 라이브러리 구조를 따른다. `pai