왜 Domain Layer는 순수해야 하는가?회사에서 코드 리뷰를 받다가 domain layer 는 특히 더 중요하게 리뷰하고 있다는 조언을 듣게 되었다. Domain Layer의 Domain Model을 만들었다고 생각했는데, 실질적으로 살펴봤을때 데이터베이스의 테이블을 Kotlin Class로 옮겨놓은 것 뿐이었다. 도메인의 행위와 규칙은 없고, 데이터 구조만 남아있었다. 예를 들어서, 주문 테이블이 있었다고 치면 테이블의 컬럼들은 아래와 같을 것이다.orders- id- user_id- status- total_price- created_at- updated_at이걸 그대로 옮기면 Order 라는 도메인 모델처럼 보이지만, 실제로는 orders 테이블을 표현한 데이터 구조가 되어버린다. data c..
Backend
Kafka 실패 메시지는 어떻게 다시 처리할까kafka 를 이용하여 컨슘한 메시지를 처리하고, 완료 후에 이벤트를 발행하는 서비스를 개발하였다. 개발 환경에서 테스트를 할 때는 DLQ 로 빠지는 이벤트가 없었지만, 실제 운영 환경에서는 msa 서버간의 통신 중에 timeout 이 종종 일어난다거나 Database 증설 작업 등으로 순단이 발생하였다. 현재 내가 개발한 서비스는 event 처리에 실패하면 DLQ topic으로 메시지를 보내고, 원본 topic의 offset은 진행된다. 다만 DLQ topic을 별도로 consume하는 로직은 아직 없고, 운영자가 API를 직접 호출해서 필요한 비즈니스 로직과 event 발행을 수동으로 수행하고 있다. 이 구조는 당장에 처리해야할 메시지를 정상적인 것 처럼 ..
1. 배경 기존 서비스에서 신규 서비스로의 회원 데이터 이관을 위해, 이벤트 기반 아키텍처를 도입하였다. 회원이 처음 신규 시스템에 접근하고 이관에 동의하면, 회원 서비스는 MemberMigrated 이벤트를 발행한다. 이 이벤트를 트리거로 데이터 이관이 비동기적으로 수행되는 구조를 설계하였다.2. 초기 설계 초기에는 다음과 같은 구조를 사용하였다.Kafka Consumer가 MemberMigrated 이벤트를 수신내부 API 호출API는 즉시 202 Accepted 응답 반환실제 데이터 migration은 비동기로 수행migration 완료 후 migrationDone 이벤트 발행이벤트 기반이며, 데이터 마이그레이션이 필요하므로 API 응답을 항상 빠르게 처리하고 비동기적으로 데이터 마이그레이션을 처리..
외부 SaaS와 우리 서비스를 연동하는 개발을 하면서 가장 많이 마주친 개념은 인증과 인가였다. 이번 글에서는 SSO, JWT, 그리고 인증/인가가 실제 요청 흐름에서 어떻게 연결되는지 정리해보려고 한다.1.인증 (Authentication) 이란“너 누구야?” 라고 사용자가 누군지 확인하는 단계이다. 즉, 요청을 보낸 주체가 누구인지 식별하고, 그 신원이 유효한지 검증하는 단계다.아이디 / 패스워드로 로그인Google, Kakao 등을 통한 소셜 로그인회사 SSO 를 통한 로그인인증이 끝나면 서버는 “이 사용자가 누구인지”는 알 수 있다. 하지만 그 사용자가 어떤 기능까지 수행할 수 있는지는 아직 알 수 없다.2.인가 (Authorization)인가 라는 것은 “너 이거 해도 돼?” 를 확인하는 과정이..
분산 메시지 큐1. 메시지 모델1. point-to-point model : 전통적인 메시지 큐. 큐에 전송된 메시지는 한 소비자만 가져갈 수 있음. 메시지를 가져갔다는 뜻으로 ACK 를 보내면, 큐에서 해당 메시지가 삭제됨. 2. publish-subscribe model : 토픽에 메세지를 보내고 토픽으로부터 메세지를 받음. 메세지는 해당 토픽을 구독하는 모든 소비자들에게 전달됨. 메세지는 토픽에 보관됨. 토픽을 여러 파티션으로 나눠서, 메시지를 균등하게 각각의 파티션에 보내어 분산 배치함. 2. 문제의 설계 요구 조건 메세지 큐의 기본 조건 : 생산자는 메시지를 큐에 보내고, 소비자는 큐에서 메시지를 꺼낼 수 있어야 한다. 기본 기능 외에도 성능, 메시지 전달 방식, 데이터 보관 기간 등을 고려해야..
Intro 비즈니스는 오래 가고, 기술은 자주 바뀐다데이터 엔지니어에서 백엔드 엔지니어로 전향하면서, 코드로 해결해야 하는 문제가 훨씬 더 많아졌다. 지금은 MAU가 꽤 나오는 글로벌 이커머스 백엔드를 만들고 있다. 트래픽도, 요구사항도, 의존하는 외부 시스템도 많다 보니 코드베이스는 빠르게 커지고 복잡도도 함께 증가한다.그래서 단순히 “기능을 구현하는 방법”이 아니라, 유지보수와 확장에 강한 구조를 더 진지하게 공부해야겠다고 느꼈다. 이 글에서는 현재 실무에서 백엔드 개발에서 자주 사용중인 Usecase 패턴을 중심으로, 실제 이커머스 도메인에서 어떻게 적용할 수 있는지 정리해보려 한다. 전통적인 Controller–Service–Repository 구조의 한계전통적인 Backend pattern 은 ..
Intro 현재 글또 9기에서 만난 팀원들과 같이 Resumait 라는 제품을 만들고 있다. CS 스터디를 진행하다가 뜻이 맞아서 사이드 프로젝트까지 개발을 하게 되었다. Resumait 은 LLM 을 기반으로, 사용자의 이력서 작성을 도와주는 서비스이다. Resumait는 특히 주니어 및 경력 개발자를 주요 타겟으로하고 있다. 개발자 채용 시장 특성상 하나의 이력서를 기반으로 여러 회사의 공고에 지원하게 되기 때문이다. 뿐만 아니라, 종종 개발자에게도 자기소개서를 요구하는 경우가 많다. Resumait 에서는 Credit 이 서비스를 사용할 수 있는 단위이다. 사용자는 본인의 이력서를 기반으로 자기소개서 글을 작성할 때 Credit 을 소비하게 된다. 그런데 유의할 점은 이 Credit은 사용자가 R..
1. entities.pyfrom uuid import UUIDfrom advanced_alchemy.base import UUIDAuditBasefrom sqlalchemy.orm import Mapped, mapped_columnfrom sqlalchemy.dialects.postgresql import UUID as psql_UUID # postgresql 에서도 string type 이 아닌 UUID type으로 관리하기 위해 importclass IdempotencyKey(UUIDAuditBase): idempotency_key: Mapped[UUID] = mapped_column(psql_UUID(as_uuid=True), unique=True, nullable=False) 2. auto..