[Yabam] 야밤 프로젝트 운영 회고

2025. 9. 10. 18:12·회고

서론

야밤 실제 사용 이미지

야밤은 컴공 학창 시절에서 가장 기억에 남고 애정이 가는 프로젝트였다.

 

대학교 축제를 위한 테이블 오더 플랫폼인 야밤은 기획부터 점주들에게 사용 유치를 하고 실제 축제에서 배포하고 사용자들의 피드백을 듣기까지 너무 재밌었고 설렜었다.

 

축제하는 주에는 혹시 버그가 있지 않을까 잠도 못자며 집에서 로그를 확인했다.

 

마지막 날에는 축제 주막 안에서 사용자들의 피드백을 받으면서 생각하지 못한 부분을 고민하기도 하고 잘 사용하는 모습에 뿌듯해 하기도 했다.

 

이번 글은 이런 야밤 백엔드 프로젝트에서 개발 - 배포 - 운영까지 전체적으로 어떻게 설계했는지 방법을 알려주고 회고하는 글이다.

 

야밤 서비스의 특징

  • 야밤 서비스는 대학교 축제를 위한 테이블 오더 플랫폼이다.
  • 대학교 축제 특성상 정기적인 트래픽이 발생하지 않으며 유명 연예인이 오는 날이나 다수의 대학교가 축제가 몰리는 날에는 트래픽이 정상적인 날보다 몇 백배가 발생할 수 있다.
  • 최대 트래픽을 예상하고 Scale-up 을 하여 최고 사양으로 운영을 하면 좋겠지만 비용과 한계가 명확하기에 야밤 서비스는 유연한 확장과 핵심 도메인 서비스의 독립적인 확장을 중요시한다.

 

야밤 코드 레밸의 구조

Modular monolithic Architecture

  • 초반 MSA 로 구조를 잡는 것은 팀원의 이해도나 개발의 진행도를 봤을 때 과도한 엔지어링으로 판단
  • 하지만 완벽한 MSA는 아니더라도 추후 필요성에 따라 서비스를 분리할 계획을 가지고 있음 (특정 도메인에 트래픽이 많이 몰리므로)
  • 팀이 gradle - multi module 에 대해 이해도가 충분하다고 판단하여 monolithic 서버로 배포가 되지만 모듈을 분리하여 개발을 진행하는 Modular monolithic 구조를 채택함

구조에 대한 컨벤션

  • 야밤 프로젝트는 다음과 같은 기준으로 모듈을 분리함
  • application 모듈: 배포되는 서비스 단위로 분리되는 모듈, 서비스에 필요한 모든 라이브러리를 implementation 형식으로 의존함
  • domain 모듈 : 야밤 서비스의 도메인을 투영하는 모듈, 외부 모듈, 라이브러리의 의존을 제거함 (common 모듈이나, compileOnly 수준의 트레이드오프는 몇몇 허용)
  • infra 모듈 : 야밤 서비스의 외부 인프라를 기준으로 모듈을 분리, Domain 의 인터페이스를 구현해야 함
  • common 모듈 : 위 3개의 모듈의 성격을 가지지 않으면서 공통적으로 사용해야 하는 모듈(exception, log…)
  • utils 모듈 : 다른 라이브러리로 대체될 수 있는 공통 로직을 담음 (웬만해서는 static 메서드로 설계)

 

아키텍처

  • Controller → Service → Repository 로 구현되는 Layerd Architecture
  • Domain 을 모듈로 분리함으로써 Infra 와 분리된 interface 를 Infra 모듈에서 구현하도록 함
  • Domain Entity 와 Infra Entity 를 분리함 (이 이유와 효과는 뒤에서 다룸)

 

Domain Entity 와 Infra Entity 분리 이유와 회고

현재는 Domain Entity 와 Infra Entity(JPA 같은) 를 분리하여 구현했다.

 

Domain 엔티티는 기본 Java 로 이루워져 있고 별다른 라이브러리 없이 Domain Model을 투영한 코드이다.

 

설계 초기에 Domain Entity 와 Infra Entity 를 분리한 이유는 Domain을 Infra 와 완벽한 분리를 원해서이다.

하지만, Domain Entity 와 Infra Entity를 분리한 형태의 개발에 대한 팀의 이해도와 경험이 충분하지 않았다.

 

따라서 발생한 코드는 다음과 같다.

@Getter
public class Order {
	private Long orderId;
	private OrderStatus orderStatus;
	private Integer totalPrice;
	private String description;
	private Receipt receipt;
	private LocalDateTime createdAt;
	private List<OrderMenu> orderMenus;

	... 생성 메서드
	
	public void setOrderStatus(OrderStatus orderStatus) {
		this.orderStatus = orderStatus;
	}

	public void setCustomOrder(Order patchOrder) {
		this.totalPrice = patchOrder.getTotalPrice();
		this.description = patchOrder.getDescription();
	}
}

위 코드는 자신이 짠 코드가 아니지만 대부분의 Domain Entity 가 위처럼 구성되어 있다.

 

Jpa Entity 처럼 필드와 생성 메서드를 작성하고 Service 에서 도메인 로직을 수행하는 Transaction Script 방식으로 개발하고 있다.

 

이는 객체지향에 대한 충분한 이해도와 도메인 문서화하고 코드로 투영하는 경험이 부족해서 발생하였다.

 

추가로 JPA Entity 로도 도메인 엔티티처럼 사용이 가능하고 충분히 도메인 로직을 담긴 엔티티를 설계가 가능하다.

만약 Infra 를 바꾼다고 하더라도 Jpa 어노테이션만 제거하면 된다.

 

이 방법을 사용하지 않은것은 팀의 JPA 를 객체지향적으로 사용하는 방법에 대한 이해도가 부족해서였다.

리팩터링을 시도했을 때도 이 방법을 사용하지 않을 것인데 이유는 현재 Domain Entity에 점진적인 리팩터링을 원해서이다.

 

  • +팀원과 같이 코드의 구조와 문제점에 대해 계속 이야기하면서 도움이 됐던 깃허브 레포 혹은 참고자료이다.
    • 조영호 : https://github.com/eternity-oop/DDD-essence-part2
    • 토비 : tobyspringboot/splearn: 토비의 클린 스프링 강의 예제 - Splearn
    • 조영호 : 객체지향의 사실과 오해

 

ArchitRulesTest 도입

뒤에서 다루겠지만 팀 내의 아키텍처 컨벤션을 결정하기만 하고 검증하는 방안이 없다면 오염되기 쉽다고 판단했다.

 

그러던중 ArchitRulesTest 툴을 알게 됐고 팀원과 같이 학습 후 도입할 예정이다.

 

지금은 대규모로 리팩터링 중이라 도입을 안하고 있지만 리팩터링이 끝나면 본격적으로 도입 예정이다.

 

Yabam Infra 구조

 

배경

  • 아무래도 학생 신분이다 보니 클라우드를 활용하는데 비용적 부담이 있었습니다.
  • 마침 학교 연구실에 안쓰는 라즈베리파이 10대 이상이 있었고 해당 라즈베리파이를 이용해 온프레미스를 구축한 배경을 가지고 있습니다.

Services

  • 야밤 서비스는 핵심(core) Service 와 인증을 담당하는 auth 서버로 이뤄져 있다.
  • 점주 PoS 실시간 기능을 위해 구비된 SSE 채널 서버가 존재한다.
  • auth 서버를 따로 둔 이유는 추후 Core 서버를 분리한다면 auth server를 분리하는게 유연할거라고 판단해서이다.
    • 설계 초기에는 오버해 보이지만 서비스 내에 인증 기반은 점주 기능이 대다수이고 인가 로직은 gateway 에 공통처리 했다.
    • Passport 패턴을 사용해서 auth 로 인가를 요청하는 로직은 포함되어 있지 않다.

Service Mesh

  • 운영 간편화를 위해 Service Mesh를 사용했다.
  • gateway 사용 이유:
    • 여러 서비스가 유기적으로 배포되고 다중화된 서비스에서 자체적인 로드밸런싱 필요성을 느낌
    • Nginx 와 같은 리버스 프록시를 선택했어도 됐지만 Spring Cloud 에서 제공하는 gateway 를 사용해도 된다고 판단
    • passport 패턴 같은 로드밸런서이자 공통 로직을 앞단에서 처리할 수 있도록 커스텀이 가능에 이점을 느낌
  • discovery 사용 이유:
    • 여러 분산되는 서비스들의 물리 주소를 추상화 시킬 컴포넌트가 필요했음
    • K8S나 docker swarm 같은 오케스트레이션 툴도 후보군 중 하나였지만 서비스의 크기를 고려했을 때 러닝커브도 낮고 호환도 잘되는 Spring Cloud Discovery를 선택함

 

Yabam의 CI 파이프라인

코드 컨벤션 검사

  • 동료와 협업을 하다보면 동료의 코드 스타일이 실수이든 혹은 나쁜 습관이든 오염된 코드를 커밋할 수 있다.
  • 물론 동료라면 코드 리뷰를 통해서 이를 확인해주고 정해 놓은 컨벤션을 어긴다면 지적하고 의견이 다르다면 논의하는 과정을 거치는 것이 바람직하지만
  • 사람의 리뷰에는 한계가 있다고 판단했다.

캠퍼스 핵데이 Java 코딩 컨벤션

  • 그러던 중 린트 검사라는 좋은 방식이 있어서 팀과 협의 후 도입했다.
  • naver github 에서 제공하는 컨벤션을 따르는 오픈소스를 가지고 왔고 build 하거나 혹은 에디터에서 자동으로 컨벤션을 잡아주는 환경 세팅을 진행했다.
  • 덕분에 사소한 코드 오염을 잡고 일정한 코드 퀄리티를 유지할 수 있었다.

 

spotbugs로 버그 지점 확인하기

버그에 대한 대비책으로 가장 효과적인 대비는 무엇일까 팀과 계속 고민했다.

 

“버그 없는 코드가 제일 좋은 방법 아닌가?”

버그 없는 코드를 짠다면 해결되겠지만 인간이라면 그런 코드를 작성하는게 가능한 영역인가? AI 가 발전한다고 하더라도 가능할까?에 대한 생각이 들었다.

 

가장 이상적인 대비책이 안된다면 가장 안좋은 버그 대비책은 무엇인가?

바로 런타임시에 발견되는 버그이다.

 

그렇다면 컴파일시에 발견된다면 좋지 않을까?라는 생각을 했다.

팀원과 서핑을 하면서 Spotbugs라는 툴을 사용하는 사례를 보고 현재 프로젝트에 도입할 예정이다(아직은 기존 코드가 있기에 좀 더 검토중)

spotbugs를 통해서 컴파일 빌드 시점에 NPE 같은 지점을 파악할 수 있겠다 싶었다.

Test 코드 열심히 짜기

spotbugs 는 사소한 버그 지점을 찾아주는 것이고 비즈니스 로직 관점에서의 오류는 개발자가 Test 코드를 열심히 짜야한다.

 

build 시점에 Test를 다 통과하는지 검사하기 때문에 Test 코드를 통해 프로그램의 오류를 런타임 시점에 발견되지 않도록 팀내에 핵심 컨벤션으로 간주했다.

AI 활용

이제 최대한 배포하기 전에 오류를 찾을려하는 시도를 했는데 이 지점까지 체크하지 못하는 에러나 문제 코드는 우리 팀의 동료가 리뷰시에 검토해야하는 영역이다.

 

하지만 이 또한 사람한테 맡기는 영역이라서 팀원의 개발 영역 외의 요소에 리뷰 퀄리티가 들쑥날쑥하다.

 

그래서 요즘 유행하는 AI를 활용하기로 했다. copilot 이나 coderabbit 을 고려했는데 coderabbit 평이 좋아 도입하였다.

 

개인적으로 coderabbit 에 대해 너무 만족하면서 사용하고 있다. 리뷰해주는 입장에서도 놓친부분을 어느 정도 잡아주기도 하고 pr 요약도 해줘서 너무 편했다.

남은건 책임감

사실 학생 수준인 나 그리고 팀의 경험에선 최대한 CI 파이프라인의 퀄리티를 높이려 했다고 생각한다.

 

하지만, 이렇게 높였으니 리뷰 정도는 가볍게 해도 되겠지? CI 통과했으니 대충 pr 올리면 되겠지? 라는 생각을 안하도록 팀원과 꾸준히 이야기 나눈다.

 

더욱이 PR 템플릿을 열심히 작성하고 문서화를 남기려고 노력한다. 위에서 언급했듯이 초반 설계에 대한 실수를 바로잡으려 리팩터링을 하고 있는데 하면서 문서화도 많은 시간을 들여서 작성하고 있다.

 

또한 “내가 개발하는 도메인이 아니더라도 같은 이해도를 가져야 한다”가 우리 팀이 추구하는 방향성이라 생각한다.

 

하지만 인간인지라 예전 pr 내용이나 나눴던 나 외의 팀원 도메인 이야기는 기억하기 힘들다 그래서 PR 템플릿과 문서화를 중요시해야한다고 생각한다.

 

따라서 나름 체계적인 CI 파이프라인에서 남은건 책임감이라 생각한다.

Yabam 배포 구조

CI/CD

  • 기본적으로는 위와 같이 배포 파이프라인을 구성했다.
  • dev 서버는 라즈베리파이 하나에 모든 서비스와 db들을 올려놨고 깃액션 CI가 통과되면 jenkins로 webhook 을 날리고 자동 배포를 한다.
  • production 서버는 merge 하는 담당 개발자가 jenking를 이용해 직접 deploy 한다.

무중단 배포

  • Service Mesh 를 이용해서 무중단 배포를 구현하였다.
  • 실제로 축제 당일에도 버그가 있어서 기능을 수정하고 버전업을 하는 일이 있었다.
  • 버그가 없도록 개발하면 좋겠지만 항상 버그 혹은 요구사항에 맞는 기능 수정을 할거라고 예상을 하고 무중단 배포 파이프라인을 구축해뒀다.

  • Service Mesh 덕분에 버전업을 하면서 재배포를 하거나 확장을 위해 새로운 서버를 추가해도 서비스 컴포넌트 제외하고 수정을 유발하지 않고 유연하게 배포할 수 있었다.
  • Spring 은 graceful shutdown 을 지원하기 때문에 사용자한테는 항상 가동되는 서비스가 내부적으로는 재배포되는 무중단 배포를 구현할 수 있었다.

  • 버전업을 해서 잘 실행되던 서비스가 버그가 발생할 수 있는 것을 고민했다.
  • Obserability 를 고려해 구축한 prometheus 와 grafana 를 사용해서 API 실패율에 따른 알람 요청을 디스코드로 확인할 수 있게 했다.

  • 위처럼 오류를 확인하면 developer는 jenkins에서 작성되어 있는 버전업을 롤백하는 스크립트를 실행시키도록 구성했다.

Scale out 회고

  • service scale out 을 했을 때 예상치 못한 곳에서 장애가 발생한적 있었다.

  • 해당 service의 MySQL 서버가 장애가 발생했는데 이유는 다음과 같다
  • HikariCP 의 갯수 수치를 조정을 안했던 것이다.
  • 커넥션 풀을 조정 안하면 service 가 1개 였다가 2개만 되더라도 2배가 된다.
  • 커넥션 풀이 active 커넥션으로 꽉찬다면 MySQL 은 기존 연산보다 2배를 더하게 되어 CPU 가 터졌다
  • MySQL 서버의 Cpu 코어 수와 서버 수 x cp 사이즈 를 계산하여야 한다는 경험을 얻었다.

 

맺음말

야밤은 제일 애정 깊은 프젝인 만큼 팀원과 같이 운영에 대해 많은 이야기를 나누면서 설계했다.

 

하지만 견해가 적었기 때문에 실수도 많이하고 실제로 축제때 이렇게 운영했다면 더 나앗을텐데라고 아쉬움도 남는다.

 

그래도 축제 끝나고 팀원과 회고하기도 하고 더 좋은 방안을 찾아보면서 더 성장할 수 있어서 만족했다.

'회고' 카테고리의 다른 글

[Yabam] Kafka 에서 Redis Pub/Sub migration 구현과 회고  (0) 2025.06.19
[Yabam] SSE 도입을 통한 RDB 부하 감소 회고  (1) 2025.06.17
[회고] 백엔드 공부 회고(2024)  (1) 2025.01.02
[회고] 멋쟁이사자처럼 12기 중앙 해커톤 회고록 ( 백엔드 관점 )  (0) 2024.08.15
'회고' 카테고리의 다른 글
  • [Yabam] Kafka 에서 Redis Pub/Sub migration 구현과 회고
  • [Yabam] SSE 도입을 통한 RDB 부하 감소 회고
  • [회고] 백엔드 공부 회고(2024)
  • [회고] 멋쟁이사자처럼 12기 중앙 해커톤 회고록 ( 백엔드 관점 )
천방지축 개발자
천방지축 개발자
기록용 블로그
    250x250
  • 천방지축 개발자
    KKanging
    천방지축 개발자
  • 전체
    오늘
    어제
    • 분류 전체보기 (127)
      • 백엔드 (32)
        • Spring framework (4)
        • SpringSecurity (6)
        • JPA (4)
        • DB (6)
        • Spring Cloud (2)
        • CI CD (0)
        • 아키텍처 & 패러다임 & 디자인 패턴 (6)
        • 시스템 설계 & 성능 개선 (4)
      • JAVA (10)
        • 언어 (3)
        • JVM (4)
      • cs (57)
        • 운영체제 (8)
        • 컴퓨터네트워크 (6)
        • 자료구조 (15)
        • 알고리즘 (8)
        • http (20)
      • 인공지능 (5)
        • Reinforcement Learning (5)
      • 기타 (9)
        • 백준 (2)
        • python (2)
        • 백준 장학금 (5)
      • 회고 (5)
        • 토덕-리팩터링 (2)
      • 아티클 (0)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    점근적 표기법
    알고리즘
    연결리스트 종류
    heapq
    jpa n+1 문제
    엔티티 그래프
    멀티프로세서
    AVL트리
    HTTP
    백준 장학금
    프로세스
    연결리스트
    posix
    힙트리
    JVM
    python
    spring
    스케줄링
    SpringSecurity
    백준장학금
    강화학습
    이분탐색이란
    Kruskal
    JPA
    최대 힙
    자료구조
    완전이진트리
    최소힙
    운영체제
    MSA
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.4
천방지축 개발자
[Yabam] 야밤 프로젝트 운영 회고
상단으로

티스토리툴바