NHN Forward22 후기

Paul·2023년 4월 1일
0
post-thumbnail

11월 24일에 NHN에서 주최하는 기술 컨퍼런스에 다녀왔다.
올해 5번 째로 개최되는 행사이며 다양한 세션과 이벤트가 준비되어 있었다.

키노트

NHN에서 진행 중인 여러 사업에 대한 소개와 비전을 발표했다. 게임, 데이터 플랫폼, 클라우드, AI와 같은 다양한 분야에서 활약 중인 것을 알 수 있었다.

무엇보다 기술을 중요시하는 것이 느껴졌으며, 엔지니어의 성장을 지향하고 좋은 기술 문화를 지닌 회사라고 느껴졌다.

편안한 휴식 시간을 지켜줄 안정적인 백엔드 운영과 개발 기법

🔁 자동 재시작
NHN Dooray에서는 개발한 사람이 운영도 함께 진행한다고 한다. 그래서 서버가 터질 수도 있다는 부담감과, 이를 해결하기 위해 상시 노트북을 들고 다녔다고 한다. 그로 인해 피로감이 증가하였고, 자동 재시작(Self-Healing)을 적용하였다.

초기엔 자동 재시작을 위해 bash와 cron을 활용했으며 jvm 실행 옵션을 통해 개선했다고 한다. 추후엔 Kubernetes와 Spring Boot의 Actuator를 통해서 주요 기능(Disk, Datasource 등)을 점검하고 위험에 대응했다고 한다.


🤯 과부하 처리하는 방법
소수 서버의 문제가 다른 서버의 문제로 이어지며 점진적으로 연결된 서버에 오류를 유발하는 cascading failures를 소개하며 과부하를 처리하는 방법에 대한 내용도 포함되었다. 이를 해결하기 위해 지표를 측정하고 가시화하기 위해, Spring Boot와 Actuato를 활용하고 Grafana를 연동했다고 한다.

대응 방안으로는 여러 가지가 있었지만, 비즈니스 특성을 분석하여 수신 거부(HTTP Status Code: 409)를 활용했다.


💴 백엔드에서 Http 캐시 활용
백엔드에서 HTTP 캐시를 활용하는 방법에 대한 내용도 포함되었다. HTTP의 Cache-Control 헤더와 ETag 헤더를 사용하여 캐시를 적용할 수 있었다. GET 메소드에서만 사용되며, 인증 등의 변경될 가능성이 있는 데이터를 사용해서는 안 된다.


🔙 Netty의 Writing Back Pressure 구현
클라이언트의 성능이 좋지 않거나, 네트워크 성능이 느린 경우 소켓 서버의 OutOfMemoryError가 발생할 수 있다. 이를 해결하기 위해 Netty의 Writing Back Pressure 구현 방법도 소개했다. 클라이언트의 상황에 따라 응답 시간을 제어하는 것이 포인트이다.

백엔드 개발자의 편안한 휴식을 지키기 위해선, 성능 개선으로 기능 장애가 발생하지 않도록 테스트 코드를 지속해서 작성해야 한다. 개선에 대한 지표를 확보하여 가시화를 해야 하며, 반복 작업은 자동화를 하는 것을 권장한다.


🍱 점심 시간


분산 시스템에서 데이터를 전달하는 효율적인 방법

📮 데이터 전달 보장 방법론
분산 시스템이란 목표를 달성하기 위해 여러 개의 컴퓨터 리소스를 사용하는 시스템을 말하며, 시스템은 두 개 이상의 컴포넌트로 구성되어 있다.

데이터를 전달하는 방법은 두 가지이며, Remote API를 이용하는 방식과 MessageQueue를 사용하는 방식이 있다. Remote API를 사용하는 경우, 서버-클라이언트 구조이며 사용자 요청에 즉각 요청하는 API에서 주로 사용한다. MessageQueue를 사용하는 방식은, Publisher-Consumer 구조이며, 배치 작업, 비동기 작업에서 주로 사용한다.

분산 시스템에서 컴포넌트들은 네트워크로 연결되어 있으며, 네트워크는 신뢰할 수 없는 매체기에 항상 데이터 유실에 대비해야 한다. 데이터 전달 보장 방법은 At most once(최대 한번), At least once(최소 한번), Exactly once(정확히 한번) 세 가지이다.


🗂 RDB를 사용하는 애플리케이션에서 전달 방법
스프링 프레임워크에서 제공하는 @Transactional과 트랜잭션 Commit 이벤트를 사용하는 방법이 있다. 또한, Transactinal Outbox Pattern과 Polling Publisher 패턴을 적용하여 RDB를 Message Queue로 사용할 수 있다.

RDB를 사용하는 경우, REST-API 환경에서 At-least-once를 구현할 수 있다. 하지만 Polling, Publisher 과정으로 인한 지연 처리가 발생하며, 데이터베이스에 부하가 생긴다.


🐇 RabbitMQ를 사용한 전달 방법
RabbitMQ란 AMQP(Advanced Message Queue Protocol)을 구현한 메시지 브로커이며 Publish/Subscribe 방식을 지원한다. Producer Confirm 과정과 Consumer Acknowledge 과정이 필요하며, DeadLetter Queue를 사용한다.


🚄 Kafka를 사용하는 애플리케이션의 전달 방법
RabbitMQ와 유사한 방식이며, 마찬가지로 Producer Confirm과 Consumer Ack를 구현해야 한다.

API 우선 접근 방식과 OpenAPI Specification

API 설계 방식에는 API 우선 방식(API First), API 설계 우선 방식(API Design First), 오픈 API 스펙(OpenAPI Specification)이 있다.

☝🏻 API 우선 접근 방식
API 우선 방식은 API가 서비스의 핵심이며, 서비스 성공을 위한 전략이다. 반대말은 Code First라고 한다.


✌🏻 API 설계 우선 접근 방식
API 설계 우선 방식은 방법론의 성격을 지니며, API-First를 구체화한 접근 방식이다. API Design First가 적용되기 위해서는 이해 관계자끼리 합의가 먼저 이루어져야 한다. Google에서는 API 설계 우선 방식이 업무 효율을 상승시키는 방법론이라고 말한다.


🤟🏻 OpenAPI Specification
오픈 API 스펙은 API Design First를 구체화한 방식이다. 다양한 표준 중에 De facto Standard가 Github 스타 수도 가장 많으며 표준으로써 이용된다. API 표준 스펙은 언어에 독립적이며, 사람과 기계가 이해하기 쉬어햐한다.

Swagger와 헷갈릴 수 있지만, Swagger는 OpenAPI Spec을 구현하기 위한 도구이다.

😰 시행과 착오
OpenAPI Specification을 적용하며 겪었던 시행착오 과정은 아래와 같다.

  1. 용어 통일

    • 비슷하지만 다른 기존 API(푸시, 문자, 이메일)를 통합할 때 요구된다.
    • API Facade 구조를 지닌다.
  2. API 설계 문서 관리

    • Single API Contract: JSON Reference를 통해서 중복된 항목을 추출하고 관리하며 API 문서도 Version을 사용하여 관리한다.
    • Paths Object, Path Item: Path Item 객체 아래에 파일 참조 기능을 활용하여 문서를 관리.
  3. 기능 확정

    • x- 를 통해 자바의 빈을 사용할 수 있고, OAS(OpenAPI Specification)을 관리할 수 있다.
    • API Contract -> OAS Generator -> Spring Code
    • OAS Generator에서 Mustach를 통해 yaml 파일을 읽어 확장 가능.
    • Extending Templates: OAS뿐만 아니라, OAS Generator, Mustache에 대한 이해도 필요하다.
  4. 사소한 오류

    • WebFlux 환경에서는 Mock API 사용 시에, 빈 유효성 체크가 되지 않는 이슈가 있어, PR 요청한 적도 있다.

API Contract는 완벽하지 않지만, 발전하는 중이다. 구조에 대한 이해가 필요하며 학습 비용이 요구된다. OAS 적용을 위해선, API 문서화에 대한 꺾이지 않는 마음이 무엇보다 중요하다.

클린 아키텍처 애매한 부분 정해 드립니다.

✨ 소프트웨어 아키텍처의 중요성

참고 : 클린 코드(로버트 마틴 aka 엉클 밥) & 만들면서 배우는 클린 아키텍처

소프트웨어 아키텍처 = 소프트웨어 구조
소프트웨어가 제공하는 가치 = 기능 + 구조 (어쩌면 기능보다 구조가 더 중요할지도..)

우리가 흔히 접하는 코드는 어지럽혀진 방과 유사하다. 이상적인 코드는 깨끗하게 정리된 방과 비슷한데, 이는 프로젝트 초기 상태에 흔히 볼 수 있다. 우리가 추구해야 하는 코드는 질서가 있는 코드이다.

우리는 회사에 코딩과 유지보수를 해야 하기에 좋은 아키텍처는 매우 중요하다. 코딩의 목적은 컴퓨터에 원하는 일을 시키기 위함이고, 원하는 일을 하기 위해서 코드를 읽고 수정해야 한다. 이때 구조가 잘 잡혀야 읽기도 쉽다. 좋은 구조를 가졌다는 것은 코드를 수정하기 쉽다는 것이고, 이는 인력 투입과 관련되기에 비용과도 연관된다.

기능은 구조에 의지해서 개발되어야 한다. 좋은 기능을 가졌지만 나쁜 구조를 지녔을 경우 버려지는 프로그램이며, 기능은 미흡할지라도 구조가 좋다면 개선 가능성이 있는 프로그램이라고 말할 수 있다.


👍🏻 좋은 아키텍처를 구성하는 방법

좋은 아키텍처: 쉽게 파악하고 쉽게 변경할 수 있는 규칙 by 엉클 밥(클린코드 저자)

아키텍처 패턴은 좋은 아키텍처를 위한 레시피와 유사하다. 예로는 계층형 아키텍처, 클린 아키텍처, 헥사고날 아키텍처가 있다. 패턴을 사용하기 위해서는 정석대로 따라하는 것을 목표로 하며 숙련되었을 경우, 응용과 타협을 한다.

계층형 아키텍처는 웹 -> 도메인 -> 영속성 형태의 단순한 구조이며, 보편적이고 처음 시작하는 프로젝트에 적합하다. 하지만, DB 주도 설계를 하게 된다는 단점과 복잡해지면 구조화가 쉽지 않다.

클린 아키텍처는 웹 -> 도메인 <- 영속성의 형태를 지니며 도메인이 아키텍처의 핵심이다. DDD 적용에 용이하다는 장점이 있지만, 복잡한 패키지 구조와 적은 레퍼런스로 구현이 쉽지 않다.


🛁 클린 아키텍처?

좋은 아키텍처의 특징은 관심사의 분리이며 의존성의 방향은 안쪽으로 고수준을 향한다. 주로, 다형성을 이용하여 구현하며 공부 시 쿡북을 추천한다. (만들면서 배우는 클린 아키텍처)

클린 아키텍처의 개념이 애매할 때는, 헥사고날 아키텍처를 추천한다. 레퍼런스가 많이 있으며, 클린 아키텍처와 유사하다.

계층형 아키텍처의 경우는 도메인을 최상위 패키지로 가진다. com.clean.code 하위에 controller와 service, repository 패키지가 위치한다.

헥사고날 아키텍처의 구조는 controller -> domain(usecase, domain, port) -> adapter & repository의 형태를 지닌다.


❓ 클릭 아키텍처는 애매합니다.

클린 아키텍처는 애매 그 잡채...

클린 아키텍처는 단순한 규칙이며, case by case이다. 애매함을 판단하는 기준은 아키텍처의 목표에 부합하는 지, 의존성의 방향이 고수준 정책을 향하는 지, 테스트 하기 쉬운 지, 클린 아키텍처에서 말하는 각각의 원칙들을 잘 지키고 있는 지 여부이다.

소규모 프로젝트일 경우, 팀원간의 합의가 이루어지지 않은 경우엔 클린 아키텍처를 쓰지 않는 것이 좋다. 코드가 늘어날 수 있으며, 패키지 구조가 복잡해진다.

클린 코드를 다 지킬 필요는 없으며, 적용 시 유연성이 필요하다.

Spring Cloud 기반 MSA 환경을 쿠버네티스로 전환하기

👕 샵바이 소개
Shop by 라는 MSA(MicroService Architecture) 환경의 커머스 플랫폼이 Spring Cloud 기반에서 쿠버네티스로 전환하는 내용을 다룬 세션이었다.


🤔 쿠버네티스로 전환하는 이유
전환하는 이유는 기존 Private Cloud 환경에서의 On-Premise 서비스들을 관리하기 어려웠기 때문이라고 한다. 쉽지 않은 서버 증설과, 고정적인 스케줄링, 잦은 스케일링 등이 원인이었다.

기존에는 Ansible을 활용했으며 이를 해결하기 위해, 선언적 구성으로 유연한 스케줄링이 가능한 쿠버네티스를 선택했다고 한다.


🔜 쿠버네티스로 전환 준비하기
전환 시 가장 중요하게 생각했던 부분은 크게 2가지였는데, 첫 째로 코드의 변경이 최소화해야 하는 것이고, 두 번째는 쿠버네티스의 기능을 적극적으로 활용하는 것이었다.

전환을 준비하는 과정에서 선택했던 의존성은 아래와 같다.

DependencyVM (On-Prem)Kubernetes
API GatewaySpring Cloud GatewayCloud Gateway
내부 통신Spring Cloud OpenFeignSpring Cloud OpenFeign
서비스 디스커버리Spring Cloud ZookeeperSpring Cloud Kubernetes
서비스 레지스트리ZookeeperService
프로퍼티 파일 관리Spring Cloud ConfigConfigMap, Secret
MySQL, Mongo, Kafka, Redis 등사용 중그대로 사용

🗓 쿠버네티스로 배포하기
쿠버네티스를 통한 배포는 쿠버네티스 패키지 도구인 Helm을 활용하였고, Template 변수 바인딩은 yaml로 Chart로 관리했다.


🌖 쿠버네티스로 전환하기
쿠버네티스로의 전환 시, 트래픽 전환은 GSLB를 이용하여 점진적으로 트래픽을 전환했다고 한다.

DDD 뭣이 중헌디?

😱 DDD에 대한 오해
DDD(Domain Driven Design)는 전술적 패턴이 아닌, 전략적 설계에 가깝다. 도메인 분리 시, 핵심 도메인은 In house에서 개발하는 것이 바람직하다. 특별한 아키텍처 패턴이 없는 구조에서는 DDD를 선택하는 것이 효율적이다.


🧭 전략적 설계
전략적 설계 시 유용한 도구에는 유스케이스 분석, 이벤트 스토밍, 비즈니스 모델 분석 등이 있으며 가장 중요한 것은 유비쿼터스 언어로 적극적인 커뮤니테이션을 하는 것이다.

설계전략적 설계전술적 설계
범위전반적특정 Bounded-Context
목적문제 도메인을 해결 영역으로풍부한 도메인 모델 적용
주요 패턴Bounded-Context, Ubiquitous-LanguageAggregate, Domain-Event
수행 방식접근법상대적으로 방법론에 가까움

🟩 Bounded Context

Bounded Context란? (외부 블로그 참고)

Bounded-Context 간 매핑 관계

매핑 관계설명추가정보
부패 방지 계층(ACL)도메인 모델의 손상을 방지하는 계층외부 의존 모델을 번역해서 도메인 모델로
공유 커널(SC)두 컨텍스트가 공통의 Context를 공유해서 사용긴밀한 팀에서 적용 가능
오픈 호스트 서비스(OHS)공급자 Context에서 소비자 Context에 맞게 모델 제공일반적으로 공표된 언어(PL)로 제공
분리형 노선(SW)각각 모델을 관리중복 발생
파트너십(P)두 팀이 최대한 협력매우 긴밀한 팀에서 적용 가능, 공유 커널
고객/공급자(C/S)공급자는 고객의 요구에 가능한 들어줘야 한다.갑: 소비자 Context, 을: 공급자 Context
순응자(Conformist)고객은 공급자(외부 솔루션, 레거시 등)가 주는 대로 사용해야 한다.갑: 공급자 Context, 을:소비자 Context
비동기 이벤트(Async)메시지 브로커를 통한 비동기 통합개발 난이도 상승, 결과적 일관성

콘웨이의 법칙: 소프트웨어의 구조는 해당 소프트웨어를 개발하는 조직의 구조를 따라간다.
역콘웨이의 전략: 개발하는 조직의 구조를 소프트웨어의 구조에 맞춘다.


📈 진화하는 설계
예시) 티켓 예매 시스템

  1. DDD 첫 번째 시작, 문제 공간 식별하기
    핵심 도메인을 추출하고 예매(Bounded-Context) 분리

  2. DDD 고도화, 해결 공간 확보

    • 레거시를 Bounded-Context로 분리
    • 핵심 도메인 분류
    • Model-Driven한 전술적 패턴 적용
  3. 다른 핵심 도메인 선정 후 해결 공간 확보

  4. 3번 과정을 반복하며 설계의 진화

총평

오프라인 기술 컨퍼런스에 참여한 것은 이번이 처음이었다. 혹여나 '이해하지 못하는 내용들이 많으면 어떡하지'라는 걱정이 있었지만, 다행히 그런 부분은 적었던 거 같다. 생각보다 많은 인파에 놀랐고 바닥에 앉아가면서까지 발표에 귀 기울이는 열정적인 모습들이 긍정적인 자극이 됐다.

다양한 세션들에서 나왔던 공통적인 키워드는 MSA(Micro Service Architecture)쿠버네티스였다. 개발자라면 누구나 들어봤을 만한 기술들이기에 관심은 있었지만, 아쉽게도 직접 경험해 본 적은 없었다. 이번 컨퍼런스를 통해 해당 기술에 대한 관심이 더욱 커졌으며, 성장에 대한 열망에 불을 지피는 새로운 계기가 되었다.

행사가 끝나고 집으로 돌아가는 길에 '나도 기술 컨퍼런스에서 연사가 될 수 있을까?'라는 생각이 들었다. 작년에 OAuth2를 주제로 팀 내에서 세미나를 한 적이 있다. 소규모의 세미나임에도 긴장된 탓에 말을 더듬었던 기억이 있다. 뛰어난 주제가 아닐지라도 기술을 도입한 경험이나 트러블 슈팅의 과정들을 발표하는 것은 많은 용기가 필요한 일이라고 생각한다. 나도 여러 사람 앞에서도 설명을 잘! 하는 개발자로 성장하고 싶다.

오늘도 한 수 배워갑니다.

P.S. 중요한 건 기술에 대한 꺾이지 않는 마음 😁

profile
주어진 문제를 객체지향적으로 설계하고 해결하는 것을 좋아합니다.

0개의 댓글