쇼핑몰 선물하기 기능 - syopingmol seonmulhagi gineung

선물하기 서비스 개발기

29CM 에서는 올해 1월 중순에 선물하기 서비스를 런칭했습니다.

선물하기 오픈 이벤트 링크

선물하기 서비스는 구매자와 수령자가 일치하는 일반적인 구조와 달리, 구매자와 수령자가 다를 수 있다는 것이 특징입니다.

즉, 선물하기 주문을 결제한 사람이 해당 주문을 지인에게 선물하게 되면 구매자와 수령자가 다를 수 있다는 것입니다.

일반주문 : 구매자 == 수령자선물하기 주문 : 구매자 <> 수령자

선물하기 결제 이후 구매자는 수령자에게 카카오톡이나 LMS 를 통해 선물하기 수령 가능한 페이지 링크를 전달할 수 있고, 수령자는 페이지 링크에서 선물 수락 또는 거절을 선택할 수 있습니다.

선물하기 수령 페이지 예시

수령자가 선물 받기를 실행하면 입력된 배송지로 선물 배송이 시작되고, 수령자가 선물 거절을 실행하면 구매자의 결제는 즉시 취소되고 선물을 받을 수 없는 상태로 전환됩니다.

그 외 선물하기 상태 변화에 따라 구매자 또는 수령자에게 실시간으로 카카오톡과 LMS 를 통해 적절한 메시지를 발송하는 식으로 유저에게 알람을 제공합니다.

아키텍처

아래 내용부터는 원활한 내용 전개를 위해1) 선물을 구매하여 보내는 사람은 구매자2) 선물을 받는 사람을 수령자라고 정의하겠습니다.

선물하기 서비스는 크게

  • 선물하기 주문과 결제를 실행하는 구매자 영역과
  • 받은 선물을 수락하거나 거절하는 수령자 영역으로

나눌 수 있습니다.

그렇기 때문에 서비스 계층 구조를 생각할 때, 선물하기 서비스주문 서비스가 존재한다면 아래와 같은 아키텍처를 가지는 것이 이상적인 그림이라 생각했습니다.

처음에 생각했던 이상적인 선물하기 아키텍처

선물하기의 주문과 결제, 취소와 만료, 수락과 거절, 선물하기 정보 조회, 링크 푸시 발송 등과 같은 선물하기 기능의 전반적인 책임은 Gift Service 가 담당하고, 그 과정에서의 주문 및 결제 처리는 Gift Service 가 Commerce Service 에 API 를 호출하여 처리하도록 합니다.

다만 주문과 결제 과정에서 부득이하게 발생하는 취소 (어드민 취소, 파트너사의 품절 취소) 또는 주문의 배송 처리 등은 Commerce Service 가 담당하기 때문에 Gift Service 에서는 이를 먼저 알 수는 없고, 주문과 선물하기 간의 상태 동기화를 위해 Commerce Service 에서 관련 이벤트를 메시지로 발급하고 Gift Service 에서는 이를 수신하여 선물하기 주문 상태를 최신화하는 pub/sub 구조를 적용합니다.

하지만 위의 구조를 적용하기에는 아래와 같은 이슈가 있었습니다.

  • 2020.12 현재 29CM 은 기존의 거대한 monolithic 구조를 microservice architecture (이하 msa) 로 전환하는 과정에 있고 Commerce Service 도 전환 과정에 있었기 때문에 서비스가 온전히 분리된 상태가 아니었습니다.
  • 이상적인 구조를 원한다고 데드라인이 명확히 정의된 선물하기 서비스 오픈 일정 안에 msa 분리를 병행할 수는 없었습니다. 최대한 기존 구조 안에서 이상적인 방안을 찾아야 하는 타협이 필요했습니다.
  • Gift Service 가 담당할 책임이 별도의 서비스 하나를 띄울 정도로 크지 않았습니다. 선물하기 주문의 meta 정보 및 상태 관리와 링크 푸시 발송 정도가 주요 역할이었습니다.

이런 점을 고려하여 아래와 같은 방향을 가지고 선물하기 서비스의 아키텍처를 재구성하였습니다.

  • 주문 및 결제 로직은 기존의 monolithic 서비스 (이하 api-29cm) 를 최대한 활용합니다.
  • 별도의 선물하기 서비스를 띄우지 않고, 현재 msa 전환 중이면서 커머스의 일부 로직을 담당하는 Commerce Service 에 선물하기 로직을 구현합니다.
  • 서버간 호출 과정에서 순환 참조가 발생하지 않도록 유의합니다.

그에 따라 2021.01 현재 오픈한 선물하기 서비스의 아키텍처는 아래와 같습니다.

현실을 고려한 선물하기 서비스 아키텍처

user flow 을 기반으로 선물하기 서비스 기능을 설명하면 다음과 같습니다.

  • 선물 구매자는 api-29cm 을 통해 선물하기 주문과 결제를 합니다. 이 때 선물하기 주문인 경우 api-29cm 에서 Commerce Service 로 API 를 호출하여 선물하기 주문이 발생했음을 알립니다.
  • 이 후 선물하기 주문이 완료되면 Commerce Service 에서 선물 수령자에게 전달할 선물하기 링크를 생성하고 푸시 로직을 실행합니다.
  • 선물하기 수락 링크를 열면 (위에서 예시로 제시되었던) 선물하기 수령 페이지가 뜨게되고, 수령자는 선물 수락 또는 거절을 선택합니다.
  • 선물을 수락하면 수령자가 입력한 배송지 주소가 t_order 테이블에 기록되고, 주기적으로 동작하는 주문 통보 배치가 이를 감지하여 파트너사에 배송 요청을 전달합니다.
  • 선물을 거절하면 Commerce Service 에서 api-29cm 쪽으로 결제 취소 API 를 호출하고 선물하기 상태를 거절로 변경합니다.
  • 선물 구매자가 선물 주문을 취소하거나 품절 취소 등으로 어드민에서 주문 취소가 발생할 때에는 api-29cm 쪽에서 주문을 취소하고, 취소 이벤트가 발생했음을 Commerce Service 에 알리기 위해 메시지를 생성하여 AWS SQS 에 전달합니다. Commerce Service 에서는 SQS 에 발행된 메시지를 읽어 해당하는 선물하기 주문을 취소 상태로 동기화 합니다.
  • 그 외 선물하기 주문 후 7일이 경과해도 수령자가 수락 또는 거절을 표현하지 않으면 Commerce Batch 에서 선물하기 주문을 만료처리 합니다.

구현 시 중점 사항

선물하기 서비스 구현 시에는 아래와 같은 원칙을 최대한 지켜가고자 하였습니다.

  • 기존 주문 로직을 최대한 활용한다.
  • 서버 간 역할과 책임을 명확히 한다.
  • 서버간 API 통신에서 순환 참조가 발생하지 않도록 유의한다.
  • 애플리케이션 Layer 간의 경계를 명확히한다.

기존 주문 로직을 최대한 활용한다

앞에서도 언급했지만 현재 29CM 의 서비스는 monolithic 에서 msa 로 전환하는 과정에 있고, 아직 주문과 결제의 주요한 로직은 monolithic 안에 존재합니다. 이렇게 때문에 기존 주문 로직을 최대한 활용하면서도 일반 주문과 선물하기 주문의 요구사항을 모두 만족시키고 싶었습니다.

일반 주문과 선물하기 주문의 가장 큰 차이는 주문 생성 과정에서의 필수 파라미터가 약간 다르다는데 있습니다. 일반 주문에서는 결제 과정에서 배송지 정보가 필수 값이지만 선물하기에서는 수령자가 본인의 배송지 주소를 입력하기 때문에 결제 과정에서는 배송지 주소가 필수 값이 아닙니다. 대신 선물하기의 경우는 푸시 링크 발송을 위한 수령자 이름과 휴대폰 번호가 필수 값입니다.

이런 차이를 제외하면 주문과 결제 과정에서의 큰 차이는 없었기 때문에 기존 monolithic 의 주문 및 결제 로직에서는 is_gift 라는 flag 를 두고 is_gift=true 인 경우에는 주문 과정에서 배송지 정보가 입력되지 않더라도 결제 과정에 문제가 없도록 확장하였습니다. 또한 is_gift=true 일 때에는 api-29cm 에서 Commerce Service 로 선물하기 주문이 실행되었음을 알리는 API 를 호출하였습니다.

서버 간 역할과 책임을 명확히 한다

일반적인 주문 로직은 api-29cm 에 존재하므로 주문 결제 로직은 최대한 api-29cm 에서 담당하게 하고, 선물하기 주문 완료 이후의 프로세스는 Commerce Service 에서 담당하게 하였습니다.

그에 따라 아래와 같이 서버간 역할 구분이 나뉘게 됩니다.

api-29cm

  • 주문 및 결제 처리
  • 주문 상세, 주문 내역 등의 기본적인 주문 조회
  • 선물하기 취소 처리 (구매자가 취소)
  • 배송지 입력 시 파트너에 주문 통보 처리 (api-29cm batch)
  • 선물하기 어드민 취소 처리 (품절 취소 등의 사유로 인함)

Commerce Service

  • 선물하기 주문 생성 (api-29cm → Commerce Service 로 API 호출시)
  • 선물하기 주문 조회 (선물하기 수령 페이지에서 사용)
  • 선물하기 수락 및 거절 (수령자가 선택)
  • 선물 수락 시 주문 테이블에 배송지 입력
  • 선물하기 푸시 로직 구현 및 컨트롤
  • 주문 상태와 선물하기 주문 상태의 동기화 처리 (Commerce Service batch)
  • 선물 만료 및 만료 후 푸시 발송 알림 처리 (Commerce Service batch)

서버간 API 통신에서 순환 참조가 발생하지 않도록 유의한다

애플리케이션 로직 내에서의 순환 참조는 설계상의 계층 관계가 명확하지 않을 때 발생하기 쉽습니다. 마찬가지로 서비스간의 계층 관계가 명확하지 않을 때에도 서비스간 순환 참조가 발생할 수 있습니다.

선물하기 서비스의 경우 api-29cm 의 주문 구간과 Commerce Service 의 선물하기 서비스 관련 로직에서 계층이나 책임을 명확히 하지 않을 때 순환 참조가 발생하기 쉬운 구조를 가지고 있었습니다.

이를 극복하기 위해 설계 초반에 개발자들끼리 많은 이야기를 나누었고, 몇 번의 변경 끝에 현실을 고려한 선물하기 서비스 아키텍처 로 정하고 개발에 착수하게 되었습니다.

  • api-29cm 과 Commerce Service 간의 호출은 최대한 api-29cm → Commerce Service 방향으로 한다.
  • 어드민 취소, 품절 취소와 같이 부득이한 경우에만 예외적으로 Commerce Service → api-29cm 을 허용한다.

애플리케이션 Layer 간의 경계를 명확히한다

29CM 에서는 거대한 monolithic 를 각각의 도메인 서버로 분리하면서 최대한 DDD 기반에 맞는 도메인 중심의 프로젝트 구조를 가져가고자 했습니다.

일반적인 엔터프라이즈 애플리케이션의 레이어 구성 (출처: Domain-Driven Design)

각 Layer 의 특징 및 역할은 다음과 같습니다. (도메인 주도 설계 p.72)

도메인 주도 설계에서 말하는 레이어별 특징

각 Layer 별로 역할과 목적이 다른만큼 Layer 에 속한 객체의 메서드에서 사용하는 input 과 output 도 별도로 정의하여 사용하도록 하였습니다. (물론 각 도메인 프로젝트의 성격에 따라 구현 방향을 정하는 것을 허용하되, 프로젝트 내에서는 구현간 통일성을 맞춰 주기를 기대하고 있습니다)

Layer 별로 input 과 output 형태를 명시적으로 나누고 convert 를 실행할 때, 추가되고 변화하는 요구사항에 따라 유연하게 대응할 수 있다고 생각하고 있습니다. 실제로 선물하기 오픈 과정에서도 수시로 변화하는 요구사항을 위와 같은 구조를 기반으로 비교적 쉽게 대응할 수 있었습니다. 다만 Layer 별로 convert 를 수시로 처리하면 과도한 행사 코드가 생성될 수 있지만 이는 MapStruct 또는 ModelMapper 와 같은 라이브러리를 통해 간결하게 줄일 수 있습니다. (하단의 세부구현MapStruct 활용 참고)

레이어 간 객체와 컨버터 네이밍

선물하기 상태 그래프

도메인에서 상태 (status) 정의는 최소화 하는게 좋겠지만, 선물하기 서비스의 경우 구매부터 푸시 발송, 수락, 배송, 취소 및 만료 처리, 만료 후 푸시 발송 등의 단계가 많기 때문에 도메인을 반영하는 과정에서 상태 정의도 많아지게 되었습니다.

선물하기 도메인의 상태 정의

선물하기 도메인의 상태 정의와 변경은 GiftPG 라는 도메인 객체 내에 구현하여 응집도를 높였고, 해당 도메인 코드만 보면 상태 전이가 가능한 현재 상태값을 알 수 있도록 하였습니다.

아래는 GiftPG 의 Status 와 선물 수락 시 호출되는 도메인 메서드인 acceptGift 의 코드 일부입니다.

선물하기 도메인의 Status선물하기 도메인의 수락 메서드 (acceptGift)선물 수락 가능여부 판별 로직 (availableAccept)

세부구현

애플리케이션 구현 내부를 살펴볼 때 아래와 같은 주제를 공유할 만하다고 생각합니다.

MapStruct 활용

29CM 표준에선 각 Layer 별 경계를 명확히 구분하고 있습니다.

Entity → Info → Result → Dto

보통 저렇게 Layer 마다 사용할 객체를 명시하면, 각 Layer 별로 convert 가 자주 일어나게 되며 경우에 따라서는 비즈니스 로직보다 convert 로직이 더 많아질 수 있습니다. 수시로 변화하는 요구사항에 대응하기 쉽다는 장점이 있지만 그만큼 단점도 명확한 구조였습니다. 이를 위해 여러 옵션을 검토하다가 MapStruct 를 사용하기로 결정하였습니다.

처음에는 널리 알려진 ModelMapper 를 이용할까 했으나 리플렉션 기반으로 동작되는 코드가 많아진다는 단점이 크게 느껴졌고, 그보다는 코드 생성 방식의 MapStruct를 이용하기로 결정했습니다. ModelMapper와 다르게 코드 생성 방식으로 되어있어 빌드 이후 convert 구현체를 코드 상에서 쉽게 확인할 수 있다는 장점이 있습니다.

MapStruct 에서 자동생성된 컨버터 구현체 중 일부

변경이 잦은 요구사항 중 하나는 도메인 로직의 값은 동일하더라도 프론트에서 보여지는 값을 상황에 따라 다르게 가공하여 내려줘야 하는 것들입니다.

API 응답값에 마스킹 추가 요구사항API 응답에서 사용하지 않게 된 컬럼 제거 상태에 따라 재발송 버튼 노출 여부를 보여주는 응답값 추가

도메인 로직과는 별개로 API 스펙만 변경되는 요구사항 케이스

위 요구사항들은 특정 API 응답에 대한 가공 또는 일부 프로퍼티 추가 수준의 변경 요청이며 도메인 로직의 변경은 없어도 되는 것들 입니다. 이런 요구사항들을 처리하는 것은 DDD 의 Layer 개념에서는 Interfaces Layer 의 역할이기 때문에 컨버터쪽에서 처리를 해야 한다고 결론을 내렸습니다.

MapStruct 에선 이런 요구사항들을 다양한 방식으로 해결할 수 있는데 저희는 Custom Mapper를 활용하여 문제를 해결했습니다.

아래는 MapStruct 를 활용하여 구현한 GiftDtoMapper 코드의 일부 입니다.

GiftDtoMapper 중 일부

@Named Annotation 으로도 Custom Mapping 메서드 선택이 가능한데 이 경우 String 으로 이름을 지정하기 때문에 타입 안정성을 보장하기 어렵고, 추적이 잘 되지 않아 코드를 파악할 때 문제가 많습니다.

이와 달리 별도의 Custom Annotation 만들어 convert 대상 메서드에 매핑하면 위의 방식보다는 코드 추적이 좀 더 수월해집니다.

좀 더 상세히 설명하면 다음과 같습니다.
아래 코드 이미지에서 StatusDescription 와 StatusDescriptionForUser 를 주목하여 봐주시면 좋겠습니다.

이제 아래의 GiftDtoMapper 코드 이미지에서 target 영역의 statusDescription 와 statusDescriptionForUser 를 봐주시면 좋겠습니다.

source 쪽을 보면 양쪽 모두 giftDetail.status 값을 매핑 과정에서 동일하게 사용하는데, target 쪽의 statusDescription이란 필드에는 StatusDescription.class를 이용하고 statusDescriptionForUser 라는 필드에는 StatusDescriptionForUser.class를 이용하고 있습니다. 이 때 target 에서 사용할 convert 로직은 (위의 코드 이미지에 정의된것처럼) statusDescriptionForUser Annotation 이 선언된 getStatusDescriptionForUser 메서드가 사용됩니다.

다시.. GiftDtoMapper 중 일부

위와 같은 구현으로 API 상의 일부 응답 값의 변경과 같은 요구사항을 비즈니스 로직의 변경없이 빠르게 대응 가능했습니다.

MapStruct를 사용하여 불필요한 보일러 플레이트 코드를 줄이고, Layer별 경계를 나누기 위한 매핑 클래스를 자유롭게 사용할 수 있었습니다.

도메인 PK 가 외부에 노출되지 않도록 대체키 사용

DDD 의 Entity 개념에서 식별자는 중요한 개념입니다. Entity 내에 한번 확정된 식별자는 변경될 수 없고, 식별자는 곧 Entity 이므로 외부에 유출되거나 오용되지 않도록 주의해야 합니다.

보통 DBMS 로 영속성을 관리하는 시스템에서는 DBMS 에서 제공하는 AUTO_INCREMENT 를 활용하여 Table 의 PK 로 사용하고, 이를 Entity 의 식별자로 사용하는 경우가 많습니다. 이 때 서비스 외부로 Entity 식별자를 오픈해야 하는 니즈가 있을 때, 식별자를 그대로 노출하지 않고 대체키를 생성하여 오픈하는 것이 여러모로 장점이 큽니다.

PK 기반의 식별자를 외부에 그대로 오픈할 경우

  • 가령 BigInt 타입의 유저 아이디로 서비스를 조회하는 GET API 가 있을 경우, 임의로 유저 아이디값을 변경하여 본인이 아닌 데이터를 조회할 수도 있고 (2020.05 D 사 이슈)
  • 외부 서비스와 데이터를 연동할 때 양사간의 키 값을 특정 DBMS 의 PK 로 사용할 경우, 이후 DBMS 를 이관하거나 데이터를 마이그레이션할 때 PK 까지 완전히 일치시켜야 함
  • 경쟁사에서 회사 내부에 저장된 데이터 양을 임의로 추측할 수도 있음

와 같은 여러가지 부정적인 이슈를 예상할 수 있습니다.

이런 점을 고려하여 선물하기 도메인 로직에는 PK 와 일대일로 매핑되는 token 기반의 Unique Key 를 생성하여 사용하고 있습니다. 선물하기 구매 이후 수령자에게 수령 페이지 링크를 전달할 때에는 임의의 token 을 기반으로 만들어 관련된 이슈가 없도록 하였습니다.

다만 random string 기반의 token 을 DBMS 에 넣고 인덱싱할 때에는 정수 타입에 비해 느리고 저장 공간을 많이 차지하는 이슈가 있는데 이런 단점을 보완하기 위한 훌륭한 아티클이 존재하니 해당 내용을 검토해보는 것도 좋다고 생각합니다.

Storing UUID Values in MySQL

해당 아티클을 근거로 선물하기 서비스의 고도화 시에는 version 1 타입의 UUID 에서 timestamp 값을 앞으로 재배치하여 이를 binary 처리하는 방식으로 대체키를 재생성할 예정입니다 .

AWS SQS 를 활용한 메시지 기반 통신

api-29cm 에서 취소 이벤트가 발생했을 때 SQS 를 메시지 브로커로 하여 메시지를 발행하고 Commerce 에서는 그 이벤트를 수신하여 선물하기의 Status 를 취소로 바꾸는 로직을 구현하였습니다.

원래 주문 도메인과 선물하기 도메인 간의 상태 동기화는 Commerce Batch 를 이용하여 맞추기로 했었으나, 선물 구매자가 주문을 취소했을 때 배치 주기 5분 동안은 선물 도메인의 상태가 여전히 선물 수락 가능 상태로 남는다는 것이 유저 경험상 치명적인 이슈가 되겠다는 판단이 있었습니다. (오픈 직후 발견된 이슈)

이후 개발팀은 취소 상태에 한하여 배치가 아닌 SQS 를 활용한 실시간 동기화를 구현하기로 결정하였고 처음의 우려와 달리 몇시간만에 구현과 테스트, 운영 배포까지 마무리할 수 있었습니다. (이럴 줄 알았으면 모든 케이스의 상태 동기화를 SQS 기반으로 할 걸 하는 후회가 있습니다)

이벤트 발행 메시지 표준 규격은 다음과 같이 정의했습니다. 그 중 로그 트레이싱 목적으로 사용하는 29CM 의 x-request-id 는 웹 요청이 아닌 상황에서는 수집할 수 없기 때문에 선택적으로 발송하기로 결정했습니다.

SQS 메시지 발행 Python Code

Retrofit2 활용

commerce-service 에서 api-29cm 으로 API 로 서버간 통신을 할 때 Retrofit2 을 사용하였습니다. 원래 Spring 에서 제공하는 RestTemplate 을 사용하려고 했으나 Retrofit2 와 비교해봤을 때 구현과 사용성 측면에서 Retrofit2 의 장점이 더 크다는 판단이 있었습니다.

설정과 구현이 간단하고 응답에서의 제네릭 클래스 처리도 RestTemplate 에 비해 손이 많이 가지 않았습니다. 인터페이스 기반의 코드도 직관적이라 구현자의 의도를 파악하기도 쉬웠습니다.

Retrofit2 에 관련한 아티클은 이미 많이 존재하니 API 로 서버간 통신을 구현하는 분들은 RestTemplate 말고 Retrofit2 을 사용해보는 것도 검토해보시면 좋을 듯 합니다.

LMS 발송 가능한 문자 인코딩 제한 (euc-kr)

LMS 발송 시 euc-kr 인코딩만 지원하는 국내 통신사 규격 때문에 LMS 발송 과정에서 예상치 못한 에러를 접할 수 있습니다. 실제로 29CM 에서도 선물하기 오픈 이후 LMS 발송 과정에서 간헐적인 에러가 발생하였습니다. 원인을 찾아보니 LMS 발송 메시지에 완성형 한글 영역에 없는 한글 문자가 포함된 것을 확인하였습니다. (뱡향제, 프릳츠 커피 등등)

메시지를 아예 안보내는 것보다는 일부 글자는 정확하지 않더라도 어쨌든 발송하게 하는 것이 낫다고 판단하여, euc-kr 인코딩에서 깨지는 글자는 아래와 같이 특수 문자로 대체하여 발송하도록 하였습니다.

LMS/SMS 발송 Python code

오픈 D-1

운영 환경에서 테스트하는것 만큼 정확한 테스트는 없을 것입니다. 사실 운영 환경에 배포했을 때 어떤 효과가 있는지 확인하는 유일한 방법은 운영 환경에서 테스트하는 것입니다! (클라우드 네이티브 패턴 90~91p 참고)

29CM 개발팀은 실제 오픈을 하루 앞둔 1월 20일에 운영환경에서 선물하기 전체 기능을 테스트하기로 결정하였습니다. 이를 위해 29CM 의 내부 구성원 일부만 선물하기 서비스에 접근 가능하도록 하면서 기존 주문 및 결제 과정에서는 영향이 없도록 안전장치를 마련해야 했습니다.

잘 갖춰진 msa 환경에서의 배포 프로세스에서는 카나리 배포나 피쳐 플래깅 같은 병렬 배포가 가능하겠지만 29CM 의 환경은 그런 것들을 갖춰 나가는 중이라 당장은 아름다운 모양새로 내부 구성원에게만 선물하기 서비스를 오픈 하기에는 어려운 상황이었습니다. 방법을 찾기 위한 짧고 빠른 논의 끝에 프론트 코드에 내부 구성원의 userNo 만 하드코딩하여 원하는 목적을 달성하기로 하였습니다. (그 시점에서는 가장 빠르고 확실한 방법이었다고 생각합니다)

내부 구성원의 userNo 를 수집함

이후 목표한 시점에 운영 배포를 완료한 후 모든 테스트 케이스가 성공할 때까지 테스트와 코드 수정, 환경 설정을 반복하였습니다.

오픈 D-1 배포 타임라인오픈 D-1 에 진행하는 운영 환경에서의 전체 테스트

그 과정에서 운영 환경의 API 호출이 막힌 것을 확인하여 ACL 설정을 변경하기도 했고, 예상치 못한 주문 상태값 변경을 허용하기 위해 validation 로직을 수정하기도 했습니다. 쿼리에서 중복되는 데이터가 발견되어 관련 코드를 수정하여 운영에 배포하기도 했습니다.

운영 환경에서의 ACL 설정 이슈 발견예상치 못한 상태 변경 요청 발견선물하기 상세 조회 쿼리에서 중복 데이터 발견

결국 예상했던 것보다 더 많은 시간을 써서 겨우 테스트 케이스 전체를 확인할 수 있었습니다. 그래도 유저에게 오픈하기 전에 확실하게 서비스에 문제가 없음을 확인했기 때문에 그 어떤 때보다 편하게 잠을 청할 수 있었다고 생각합니다.

고생했던 선물하기 맴버들

오픈 후 회고

목표했던 1월 21일 오전 9시에 예정했던 운영 배포를 진행하였고 사소한 이슈 몇 가지를 제외하고는 기획한대로 선물하기 서비스가 정상 동작하는 것을 확인하였습니다.

운영 배포가 완료되었음을 선언함

이후 1주일 정도의 서비스 안정화 작업후에 선물하기 팀 전체가 참여하는 회고를 진행하였습니다. 모두가 모여서 선물하기 구현 중에 느꼈던 여러 감정을 공유하는 시간을 가졌고, 유저분들이 좀 더 사랑하고 좋아할 선물하기 서비스를 만들기 위한 여러가지 아이디어를 도출하는 시간을 가져봤습니다.

오픈 전까지 주어진 시간은 짧았고 고된 작업이 많았음에도 불구하고 기쁨의 감정을 나타내는 노란색 포스트잇이 압도적으로 많았던 것에서 다들 선물하기 서비스에 대한 애정과 오픈에 대한 기쁨을 확인할 수 있었습니다. (그간의 고생은 모두 잊고.. 사람은 망각의 동물이기에 ㅋ)

이렇게 29CM 의 선물하기 서비스 소개와 개발 과정 등을 정리해봤습니다.

구성원 모두의 애정이 담긴 서비스인만큼 29CM 유저들이 사랑하는 선물하기 서비스가 되도록 지속적인 개선과 운영을 약속드립니다.

이 글을 보시는 분들도 29CM 앱을 열고 고마운 분들에게 선물하기를 해보세요.

29CM 와 선물하기 서비스 많이 이용해주세요.
감사합니다~

글쓴이

강호길 / 최준영 / 이지민 / 이희창

함께 성장할 동료를 찾습니다

29CM (에이플러스비) 는 3년 연속 거래액 2배의 성장을 이루었습니다.

이제 더 큰 성장을 위해 기존 모놀리틱 서비스 구조를 마이크로서비스 구조로 전환하고, 앵귤러 기반 프론트엔드 코드를 리엑트로 전환하는 등의 기술적인 시도를 진행하고 있습니다.

함께 성장하고 유저 가치를 만들어낼 동료 개발자분들을 찾습니다
많은 지원 부탁합니다!

  • 29CM 개발팀 채용 사이트