전체 글 305

빠르게, 재밌게 배우기

나는 "주어진 커리큘럼을 차근차근 밟아가며 배우는 것"을 좋아하는 타입이다.갑자기 어려운 이야기가 들어오면 어지럽고 도망가고 싶은건 나뿐만이 아닐 거라 생각한다. 두려움 인정하기. - 25년 6월 1주차 회고솔직하게 말하자면, 나의 가장 큰 컴플렉스는 “두려움”이다. 나의 회고는 대부분이 이 두려움을 컨트롤하기 위한 방향으로 나아가고 있다. 겁이 나면, 생각이 많아지고, 조금 더 많은 걸 알아dev.go-gradually.me 그리고 그때 당시 개발자 지망생들 사이에서 이런 이야기가 유행하기도 했었다.알지도 못하면서 쿠버네티스, MSA, 헥사고날 이런거 쓰면 면접에서 탈탈 털리고 서류에 대한 신뢰도도 떨어진다.그리고 회사 들어가도 그런 기술 너가 쓰지도 않는다.그런거 할 바에 스프링이나 공부 제대로 해..

[쿠버네티스 튜토리얼] 4. ConfigMap과 Secret 활용

애플리케이션 설정 관리는 애플리케이션 컨테이너화 및 배포 시 중요한 부분이다.Kubernetes에서는 ConfigMap과 Secret을 사용하여 설정값을 관리한다.ConfigMap: 환경설정 등의 비밀이 아닌 데이터를 키-값 형태로 저장하는 객체.예를 들면 애플리케이션 설정 파일, 환경 변수 등등Secret: 비밀번호, API 키 등 민감한 정보를 저장하는 객체이다.Secret의 데이터는 base64로 인코딩되어 저장되지만 기본적으로 암호화되진 않으므로 적절한 권한 관리와 필요 시 암호화 설정이 필요하다.ConfigMap과 Secret 모두 Pod에 주입할 수 있다.환경변수로 노출하거나파일로 마운트하여컨테이너 내에서 읽도록 할 수 있다. 이번 섹션에서는 ConfigMap과 Secret을 생성하고 Pod에..

[쿠버네티스 튜토리얼] 3. 서비스 노출: ClusterIP, NodePort, Ingress

Kubernetes에서 Service(서비스) 객체는 Pod들의 논리적인 집합에 단일 접근 포인트(IP 및 DNS 이름)을 제공하는 역할을 한다.앞서 Deployment로 Nginx Pod 여러 개를 띄웠다면, 클라이언트가 그중 어떤 Pod에 접속해야 할지 알기 어렵다.Service를 사용하면 여러 Pod들을 하나의 서비스로 묶고, 부하 분산(Load Balancing) 및 서비스 디스커버리(DNS)를 제공할 수 있다. Kubernetes에는 몇 가지 서비스 유형(Service Types)이 있다.ClusterIP (기본값)클러스터 내부에서만 접근 가능한 가상 IP를 부여한다.내부 서비스 용도로 사용된다.NodePort각 Node의 IP에서 정해진 포트를 열어, 외부에서 노드IP:포트로 접근할 수 있게 ..

[쿠버네티스 튜토리얼] 2. Deployment 생성과 ReplicaSet 이해

Deployment는 Kubernetes에서 애플리케이션의 배포(Deployment)와 업데이트를 관리하는 상위 리소스이다.Deployment를 생성하면 Kubernetes는 자동으로 ReplicaSet이라는 객체를 만들고, ReplicaSet이 정의된 수만큼 Pod를 생성하여 애플리케이션을 실행한다.Deployment를 통해 롤링 업데이트, 자동 복구, 배포 이력관리 등이 가능하며, 운영 환경에서 주로 사용된다.ReplicaSet은 말 그대로 Pod 레플리카(복제본) 집합을 관리하는 객체이다.“이런 Pod를 N개 running 시켜라”라는 역할을 하며, 자체적으로는 수평 확장만 관리할 뿐 업데이트 전략 등은 없다.Deployment가 ReplicaSet을 감싸서 관리한다고 이해할 수 있다. 요약하면, ..

[쿠버네티스 튜토리얼] 1. k3s로 배워보자.

서비스를 배포할 때, 롤링/카나리/블루 그린같은 무중단 배포 전략을 고려하다 보면, 자연스레 컨테이너를 다루는 방식으로 손이 가게 된다. 현재 진행하고 있는 프로젝트 Pinit의 경우, MSA로 구성되어 있어서, 무중단 배포를 위한 전략을 쉽게 선택할 수 있는 도구가 필요했다. 이때, 도커 컴포즈와 쿠버네티스를 보통 이야기하는데, 이번 기회에 쿠버네티스를 한번 쭉 써보면서 MSA의 인프라 구성에 대해 이해하고자 다음과 같이 튜토리얼을 시작했다. k3s는 Rancher사가 배포한 경량 Kubernetes로, 리소스가 적은 환경이나 로컬 실습에 적합하다.k3s는 설치가 매우 간단하며, 단일 바이너리에 Kubernetes의 핵심 기능을 모두 포함하고 있다.이 섹션에서는 Ubuntu 20.04+ 환경에 k3..

[Pinit] 최종 일관성 구현 - 회원가입 플로우, 이대로 괜찮은가?

문제 상황현재 auth 서비스와 task 서비스는 이벤트를 통해 상호작용을 조율하고 있다. 그런데, 사용자가 회원가입 후 로그인을 한 뒤 task서비스를 이용하려는데, task에 회원 정보가 존재하지 않다면 어떻게 될까? 로그인 성공 - task에 이벤트가 도착한 후task 서버에서 사용자 정보를 정상 처리정상 플로우로그인 성공 - task에 이벤트가 도착하기 전task 서버에서 auth 서버에서 처리한 사용자 정보를 아직 못받음일정을 생성할 수 없음(예외 발생)사용자의 지역 정보를 받아와야 하는데, member record 자체가 없음별명도 존재하지 않음 문제를 깔끔하게 정리해보자.현재 이 문제는 task, auth 서버 간의 문제다.사용자 경험(UX) 기준에서, 별명 정보는 필수가 아니다.진짜 문제는..

컴파일 타임 의존 vs 런타임 의존

이 말은 모듈 레벨에서도 사용 가능하고, 코드 레벨에서도 사용 가능하다.하지만 모듈 레벨 의존과 코드 레벨 의존이 별로 다르지 않다고 생각한다.말로 엄밀히 정의하는 것보단, 직접 해보며 둘의 미묘한 차이를 이해하는 것이 중요하다고 생각한다.따라서, 본 글에선 두 개념을 하나로 묶어서 정리한다.나중에 둘이 많이 다르다는 걸 알게되면 추가로 정리한 글을 작성하고 링크하도록 하겠다.컴파일타임 결합(빌드/링크 타임 결합)A 모듈이 B 모듈의 헤더/정적 라이브러리/심볼에 의존해서 컴파일·링크 단계에서 결합이 확정되는 형태예: 컴파일 타이밍에 예외를 잡을 수 있는 형태기초적인 문법 사용checked exception런타임 결합(동적 로딩/플러그인)A가 B를 실행 시점에 선택/로드/연결하는 형태예: 런타임에 예외를 ..

[Pinit] 도메인 이벤트로 소통하기 - gRPC

현재 시스템 구조현재 요구사항에는 일정 시작 시간에 푸쉬 알림을 보내야 한다는 요구사항이 있다. 하지만, 이미 일정을 진행중인/종료한 사람에게 해당 푸쉬알림이 발송될 경우, 이는 UX 측면에서 사용자의 집중력을 해칠 우려가 있기 때문에, 일정이 시작된 후에는 푸쉬 알림이 발송되지 않아야 한다. 내부 상태 변화(Schedule의 "시작됨" 상태 변화와 "취소됨" 상태 변화)를 외부 시스템이 알아야 하는 경우가 발생했다. 이렇다면, 일정 시작 시간에 발송되는 푸쉬 알림은 일정이 "시작되지 않음" 상태일 때에만 발송되어야 한다.즉, 일정의 "시작되지 않음" 관련 상태/상태 변화가 외부 시스템에 전달되어야 한다. 이에 대해 나는 다음과 같은 해결책을 고려했다.도메인 이벤트로 해결하기상태 변화 알리기 - 일정 시..

[Pinit] 푸시 알림 발송하기

FCM 토큰으로 푸시알림을 발행하려고 하자, FCM 토큰이 UNREGISTERED되어 있다는 응답을 받았다.공식 문서를 참고한 결과, FCM 토큰이 만료되었다는 의미라는 것을 알게 되었다. 토큰이 예상하지 못한 방식으로 만료될 수 있다는 것을 알게된 나는 토큰의 만료 관리 정책을 고려해야 한다는 걸 알 수 있었다.하지만, 일단 지금의 목표는 알림 발송이니, 알림 안오는 버그부터 해결하자.그렇게 만료된 토큰을 삭제하고 다시 시도해본 결과, 에러 메시지가 달라졌다.이번에는 Invalid Argument 문제를 마주했다. GPT에게 자주 발생하는 Invalid Argument 에러에 대해 물어보니 다음과 같이 답변해주었다.진짜 FCM 등록 토큰을 넣었나? 엉뚱한거 잘못 넣은 거 아닌가?토큰 문자열이 손상된 ..

gRPC 튜토리얼 - 기본 기능과 원리, 직접 해보기

시작하기 전 - RPC란?외부 서비스의 메소드를 프록시 객체를 통해 로컬 함수(시스템 내부 함수)에서 호출하는 것처럼 만들어주는 도구이다.gRPC는 Google에서 개발한 오픈소스 원격 프로시저 호출(RPC) 프레임워크로, HTTP/2 기반의 고성능 통신을 제공한다.본 글은 gRPC 공식 튜토리얼 예제를 따라가며 작성한 내용입니다.좀 더 자세한 내용은 해당 링크를 통해 확인하실 수 있습니다. 목차proto 파일 작성의존성 설정gRPC 서버 구현gRPC 클라이언트 구현빌드 및 실행Spring 확장0. gRPC의 주요 구성 요소.proto 파일을 작성한다 → (proto)protoc로 컴파일한다 → Protobuf 도구 체계생성된 코드가 메시지를 직렬화/역직렬화한다 → Protobuf 포맷/런타임gRPC는..

과거의 잘못과 반성

이 글은 아마도, 내 블로그를 주변 사람들이 모르기 때문에 쓸 수 있는 기록이다.동시에 “지난 2년이 더 이상 특별한 사건으로 남지 않았다”는 작은 선언에 가깝다.올해 6월 이전의 회고가 비어 있었던 이유도 여기에 있다. 100% 사실도 아니거니와, 100% 사실이라면 너무 부끄럽기 때문에, 소설처럼 읽어주길 바란다. 내가 스스로를 설명할 때 자주 쓰던 단어는 책임감과 감사였다.그런데 어느 시점부터 그 두 단어가 흐려졌고, 그 과정에서 후회와 반성을 겪었다.그리고 그걸 다시 찾아내게 된 과정을 담는다.시작친구가 어떤 프로젝트를 완수하면 받을 비용이 50만 원이었는데, “그 돈을 그대로 나에게 주겠다”는 제안을 받았다. 조금 알아보니, 내 입장에서는 난도가 과도하게 높지는 않았다. 바로 직전에 했던 학교 ..

2025년 12월 2주차 회고

감사가 무너지면 꾸준함이 무너진다.조급해지기 전에, 대안을 찾기전에, 충분히 감사했나?이번 주의 감사: 원 없이 MQ + 알림 시스템 개발 + gRPC 써봤다.항상 무언가를 수행하기.체계적인 상태를 유지하기.큰그림 그리고 핵심 짚기.처음부터 끝까지 차근차근 전개하기.현 시스템에 대한 감사를 유지하기.보상 체계를 깨끗히 유지하기.입장 중심이 아닌 이익 중심으로 사고하기.윈-윈 전략 생각하기.다음에 무엇을 해야 할 지 상상하기.이번주에 한 것RabbitMQ + AsyncAPI 설계 및 구현마이크로서비스 간 이벤트 기반 통신 시스템 구축 gRPC 사용법 습득푸쉬알림 기능 밑바탕 & 로직 작성이번주에 하지 못한 것DB Delete 작동 방식 깔끔하게 정리하기데이터 지역성 정리푸쉬알림 기능 버그 수정AI 모델 설..

[Pinit] 푸시 알림 Exactly-Once 구현하기

백엔드 서버 관점에서 일반적인 이벤트 발행의 Best Practice를 생각해보자.새로운 방법의 발견이 아닌, 일반적인 방법을 정리 후 적용하는 게시글이다. 트랜잭셔널 아웃박스 패턴 (Transactional Outbox Pattern)프로듀서 -> MQ 사이의 1회 이상 전송을 보장하는 매커니즘(패턴)이다.DB의 트랜잭션은 사용자의 요청과 DB 상태 변경을 원자적으로 묶어준다. 하지만, DB 상태 변경과 이벤트 기록, 그리고 이벤트 발행까지는 원자적으로 묶이지 않는다.트랜잭셔널 아웃박스 패턴은 DB 상태 변경과 이벤트 기록을 트랜잭션으로 묶는다.하지만, 트랜잭션 아웃박스 패턴은 1회 이상 발행 보장이지, 1회 발행 보장은 아니다.즉, 중복 발행 가능성이 있다.발행 후 해당 메시지를 발행함을 표기하지 못..

Java - classpath

우리가 특정 데이터를 JVM에 올리고 싶을 때, 이를 안정적으로 업로드할 "마법"이 필요해진다.이 예로는 공개 키 파일과 같은 것들이 있다.만약 키를 평범한 상대경로/절대경로로 가져오고 있다면, 이는 현재 환경에 종속적인 빌드 결과가 된다.(참고로 비밀 키와 같은 secret값들은 엄격한 운영 상황에서는 배포의 범위가 빌드 수준까지 넓어져 클래스패스에 올리지 않는다.)이때, 클래스패스라는 용어를 곧잘 접하게 된다. 클래스패스는 자바의 JVM 이 클래스를 로드할 때 사용하는 클래스로더와도 곧잘 얽히는데, 지금부터 이 클래스패스에 대해 빠르게 알아보자.1. 클래스패스란 무엇인가간단히 말하면JVM이나 컴파일러가 .class 파일과 리소스(예: .properties, .json, .xml)를 찾기 위해 검색하는..

유의미한 대화

나는 보통 내 이야기를 다른사람에게 하는 것보단, 다른 사람의 이야기를 듣는 것을 좋아한다. 사실 이런 글을 적는 것도 오만해지는 것 같아 그리 좋아하진 않는다. 단지 기록을 위해 남겨둔다. 브레인 스토밍이나 친목을 위한 대화 과정에서는 이것들이 유의미할 수 있지만, 내가 집중하고 있는 순간에는 그런 것들이 거슬리는 순간이 있다. 그럴 때 상대방에게 매몰차게 대하게 되고, "기분파"가 되는 듯한 감각, 상대방에 대한 미안함도 조금 생기게 된다. 어줍잖은 진심이 아닌 “진짜 진심”이 담기면, 나도 묘르게 말에 독기가 서리게 된다.항상 "직설적인 내용"에 "완곡한 톤"으로 말하려고 하는데, 쉽지 않은 것 같다. 이에 대한 정말 좋은 기준이 있다. "이 행동을 함을 통해, 듣는 상대방을 강제적으로 움직이..

[Pinit] 푸쉬 알림 발송해보기 - 직접 Web Push 사용

웹 푸쉬 알림은 표준 Web Push 프로토콜을 따르며, VAPID 인증 방식을 사용하여 브라우저에 푸쉬 알림을 전송한다.이때 VAPID(Voluntary Application Server Identification)는 애플리케이션 서버가 푸쉬 서비스에 자신을 식별할 수 있도록 도와주는 메커니즘이다.VAPID는 그 자체로 푸쉬 서버에게 자신을 알리는 ID가 됨과 동시에, VAPID는 공개 키 암호화 방식을 사용하여 서버가 푸쉬 요청을 보낼 때 자신을 증명할 수 있게 한다.Web Push 구현 목표브라우저에서Notification 권한 요청pushManager.subscribe() 로 endpoint, keys.p256dh, keys.auth 획득서버에서VAPID 키 쌍 생성 및 관리payload 암호화,..