2021-03-24(Wed)

항목

내용

학습 날짜

2021-03-24(수)

학습 시간

11:00~23:00

학습 범위 및 주제

TDD, 스프링부트

학습 목표

TDD와 스프링부트를 학습한다.

동료 학습 방법

-

상세 학습 내용

TDD가 실패하는 이유 - by 이규원님

팀이 아닌 개인이 실패하는 이유부터 살펴보자.

'당신은 이렇게 하지 않는다.'

Science vs Engineering. 개발자는 후자에 가깝다. 제약된 자원으로 문제를 해결해야 한다. TDD가 문제해결에 도움이 된다면 도입한다. 그런데 그렇지 않으면 도입하지 않는다. 이렇게 실용적인 판단을 해야한다.

그럼 언제 TDD를 하고 언제 TDD를 하지 않아야하나?

우리가 보호해야 하는 것

  • AWS x

  • Spring X

  • 도메인 O

우린 도메인에 가장 많은 자원을 써야한다.

우리가 제어할 수 없는 것

  • 외부 세상

    • 실 세계

    • 인프라

    • 외부 서비스

    • 레거시

따라서 우리는 우리가 보호해야할 것들을 우리가 제어할 수 없는 것들로부터 분리시킬 필요가 있다. 이를 위해 어떻게 설계를 해야할까?

설계

  • 낮은 결합

  • 높은 응집

  • 도메인 모델 보호

그런데 당신은 아마..

  1. ..구현을 테스트하고 있을 것이다.

    구현 방식을 테스트하면 어떤 문제가 생길까? 구현의 내부요소들과 테스트 케이스가 결합 되어있다. 리팩터링이라도 한번 하면 만들어놓은 테스트 케이스들이 쓸모 없어질 수 있다. 그럼 코드를 바꿀 때마다 테스트 케이스를 바꿔야하므로 비용이 늘어난다.

    어떻게 회피할까?

    정보 숨김(Information Hiding) 을 생각해볼 필요가 있다.

    On the Criteria To Be Used in Decomposing System into Modules

    Information distribution aspets of design methodology - David Parnas, 1971

    정보 숨김은 어려운설계 결정과 변경될 가능성이 높은 설계 결정을 다른 모듈로부터 숨기는 것이다.

    빈번하게 바뀌는 것들에 테스트케이스를 의존시키지 말고, 바뀔 가능성이 적은 견고한 인터페이스에 의존시키자. 구현이 아니라 설계를 테스트하자.

  2. ..레거시와 함께 살고 이를 탓할 것이다.

    원래 레거시를 벗어날 수 없다. 내가 지난 주에 작성한 코드도 레거시다. 레거시가 있는 상태에서 어떻게 TDD를 진행할 수 있을까? 깨끗함을 유지하는 코드를 감싸는 어댑터를 만들어서 레거시로부터 결합을 떨어뜨리자.

그럼 이제 팀 수준의 TDD는 어떻게 진행하는지 살펴보자.

Fail-fast 전략을 위해 아래 프로세스를 가져간다.

  • 점진

  • 반복

    • 계획

    • 실행 <-- 이것만 하면 개판된다.

    • 평가

문화도 굉장히 중요하다.

두 가지 공유가 중요하다고 본다. 1) 목표 2) 지식

아키텍쳐도 굉장히 중요하다.

  • 낮은 결합

  • 높은 응집

  • 도메인 모델 보호

    도메인 모델과 플랫폼을 생각해보자. 자바 개발자들은 테스트 얘기를 하다가도 기승전 스프링 얘기를 하는데, 그 이유가 우리에게 가장 중요한 도메인 모델을 스프링(플랫폼)에 맡겨버리기 때문이다. 도메인 모델 곳곳에 스프링 코드가 퍼져있다. 그럼 어떻게 해야하는가? 도메인 모델과 플랫폼을 분리시켜서 독립적으로 존재하게 해야한다. 플랫폼은 말 그대로 도메인 모델을 호스팅하는 역할을 해야한다. 플랫폼이 도메인 모델을 깊숙히 파고들면 안된다. 그래야 도메인 모델을 테스트하기 용이하다.

    image-20210324132225400

    이번 라이브 코딩에서 쓸 애플리케이션의 아키텍쳐다. 서비스에 카프카 쓰는건 좋은데 비즈니스 로직이랑 연결시키지만 말아라.

    MVVM에서도 view는 테스트하기 어렵지만 얇게 만들 수 있다. model, view model을 테스트하자.

우선 목적을 분명히, 구체적으로 하자.

  • 목적: 소프트웨어 사용자에게 어떤 가치를 전달할 것인가?

    • 멋진 기능 만들어주세요 -- X

    • 시각적인 데이터를 만들어서 명확하게 목적을 공유하자.

  • 분석

  • 작업 설계

    • 소프트웨어 변경은 어떤 세부 작업들이 있는가?

    • 각 작업들은 어떤 순서로 진행되어야 하는가?

    • 각 작업들은 누가 담당하는가?

자 TDD를 해보자.

image-20210324134823443
  • 코드 설계

    • 어떤 코드 변경(commit)이 필요할지 분석한다.

  • 테스트 작성

    • 코드 변경을 검증하는 자동화된 테스트 케이스를 만든다.

  • 리팩터

    • 의도 노출

    • 중복 제거

    • 추상화 수준 조절

  • 피드백

    • 단위 테스팅

    • 코드 리뷰

    • 기능 테스팅

    • 수동 테스팅

    • 사용자 반응 수집

메세지를 확인하고 테스트가 잘 작성되었다는 확신을 가진 다음 그린단계로 넘어가야한다.

Q&A

  • 단위 테스트를 넘어선 통합 테스트는 언제 만들어야할 것인가? 단위테스트 2000개쯤 있는데, 메뉴얼 테스트, 기능 테스트도 따로 한다. 외부 시스템 통합시키는 프로젝트의 경우에는 'fake 서비스'를 임시로 만들어서 제대로 통합이 되는지를 확인하기도 한다.

https://www.youtube.com/watch?v=UttzAcbuk5k

https://www.youtube.com/watch?v=cVxqrGHxutU

대규모 트래픽 애플리케이션 성능 튜닝 노하우 다 알려 드림 | 라인개발실록

Q) 애플리케이션 관점에서는 어떤 준비를 하시는지?

두 가지로 나눌 수 있다.

  1. 미리 준비할 수 있는 것

  2. 문제가 생겼을 때 대응할 수 있는 것들.

1번 예시. keep-alive를 이용해서 connection을 맺어놓고 계속 쓰는 방법을 쓴다. 프록시랑 애플리케이션 트래픽을 처리할 WAS단의 서버가 있을 수 있는데, 프록시에서 WAS단에 전달할 수 있는 Max Connection을 미리 제한해 둔다. 마찬가지로 Proxy에서 뒷단으로 전달되는 connection 수도 제한을 걸어둔다.

keep-alive를 적용할 때도 timeout, maxRequests를 설정할 수 있다. 라인 메시징 서버 같은 경우에는 짧은 시간에 트래픽이 확 오르기 때문에 maxRequests 기준으로는 여유있게 늘려주거나 아예 제한을 없애버려서 keep-alive connection이 충분히 유지될 수 있도록 설정을 해둘 수 있다.

keep-alive를 키게 되면 connection들이 연결된 채로 많이 물려 있을 수 있기 때문에 tomcat의 max connections 설정도 미리 문제가 없도록 프록시 서버들의 수준을 계산해서 늘려 놓는다.

Q) 전송하는 파일의 유형에 따라서 최적화된 서버 파라미터라는 건 너무 많다. 관리할게 많을 텐데 이런걸 다 한 명이 관리하는 건가?

아니다. 각 서비스의 특징에 맞게 튜닝한다.

Q) 모니터링하다가 문제가 생기면 어떻게 대응하는지?

모니터링 수치들, 주요 매트릭들을 보고 있다가 문제가 생겼을 경우엔 어떻게 대처할지 방법을 논의하게 된다.

일반적으로 두 가지 방법이 있을 것이다.

  1. 문제가 생겼을 때 서버로 들어오는 리퀘스트 자체를 줄이는 방법

  2. 한 컴포넌트에 문제가 생겼을 때 다른 컴포넌트에 요청을 보내지 않도록 하는 대응

서비스 관점에서 Throttling을 건다면 어떻게 동작을 하는가? 만약 VoIP 쪽 서버에 문제가 생겨서 10%만 수용할 수 있게 되었다면? 서버를 다 꺼버리는 것이 아니라, 10%라도 서버를 이용할 수 있게 하고, 나머지에게는 다시 시도해달라는 메세지를 보내게 된다.

Q) 많은 트래픽을 효율적으로 처리하는 방법은?

이 질문에 대한 답은 서비스마다 달라질 수 있을 것이다.

만약 메신저 서비스처럼 일반적인 서비스의 경우에는 활동시간대가 다를 수 있다. 또 어떤 서비스의 경우 이미지를 많이 쓰거나 영상을 많이 쓰는 등 상황에 따라 트래픽이 달라진다.

일반적으로 stateless한 서버 같은 경우 앞단에 정적(static) 콘텐츠를 서빙하기 위한 서버를 따로 빼고 CDN을 적용해 놓고 Application layer에는 Storage쪽으로 가기 전에 캐시 등을 구성하는 일반적이고 보편적인 구성들이 있을 수 있는데, 나머지들은 수치 같은 것을 좀 더 조정할 수 있을 것 같다. 어떤 병목의 쓰레드 풀(Thread pool)을 늘리는 등등!

Q) 라인에서는 비동기 처리를 어떤 로직이나 기술로 하시는지?

라인은 Armeria 오픈소스를 한번씩 봐주면 좋겠다. 퍼포먼스도 좋고 서버, 클라이언트 둘 다 기능이 있다.

Q) 라인의 장애대응방은 어디에 쓰고 계세요?

라인 메세지로 장애 대응을 할 때는 라인 웍스나 Slack을 쓰고 있다.

Q) CDN 활용에 대해서 더 자세히 알고 싶다. REST API 응답 처리도 CDN으로 상당 부분 처리 가능할 것 같은데, 신년대응 시점에는 TTL 등을 어떻게 처리하는가?

메신저는 바로 보내야하기 때문에 캐싱할 수 있는 부분이 거의 없긴 하다. 스티커 정도??

기본적으로 신년 대응을 위해 TTL을 따로 조정하는 경우는 없다. 보통 API 콜 같은 경우는 캐시할 수 없는건 non-store로 처리되고 있다.

Q) 신년 대응을 내년에 또 한다면 올해와 뭐가 달라질 것 같은가?

코로나 상황이 계속될지 여부에 따라 달라질 것 같다.

출처: 대규모 트래픽 애플리케이션 성능 튜닝 노하우 다 알려 드림|라인개발실록

키워드 모음

DAO(Data Access Object)

데이터베이스의 데이터에 접근하기 위해 생성하는 객체이다. 데이터베이스에 접근하기 위한 로직과 비즈니스 로직을 분리하기 위해 사용한다.

DTO(Data Transfer Object)

계층간 데이터 교환을 위한 자바빈즈를 뜻한다. 또한 DTO는 VO(Value Object)와 용어를 혼용해서 많이 사용하는데, VO는 읽기만 가능한 read only 속성을 가져 DTO와의 차이점이 존재한다.

일반적으로 DTO는 로직을 가지고 있지 않은 순수한 데이터의 객체이며 객체의 속성과 그 속성의 접근을 위한 getter 및 setter 메소드만을 가지고 있다.

MyBatis vs JPA

MyBatis(구 iBatis)는 SQL Mapper이다. ORM이 아니다. ORM은 객체를 매핑하는 것이고, SQL Mapper는 쿼리를 매핑한다.

SI 같은 곳에서는 Spring & MyBatis를 많이 사용하지만, 네카라쿠배 같은 자사 서비스를 개발하는 곳은 SpringBoot & JPA를 전사 표준으로 사용하고 있다.

학습 내용에 대한 개인적인 총평

TDD에 대해서 학습함과 동시에 자바 스프링부트 프레임워크에 대해서 접해봐야겠다는 생각을 실천으로 옮겼다. 기술을 선택할 때 내가 납득이 되어야하는데, 그래도 아래 이유를 통해 어느 정도 납득시키는데 성공하였다.

  • 생산성은 스프링 부트를 활용하면 타 프레임워크와 크게 다르지 않다.

    • 생산성의 끝판왕이라는 루비온레일즈는 나름 다뤄봤으니, 이번엔 개인적으로 생산성은 떨어지지만 안정적이라는 평가를 받는 스프링 프레임워크는 어떻게 구성되어있을지에 대한 개인적인 궁금함도 있었다.

  • 커리어 목표를 생각하면 스프링은 필수적으로 학습해야 한다.

자바 프로젝트는 상당히 IDE에 종속적인 느낌이라 뭔가 부자연스럽게 느껴진다. 흠 그만큼 이점이 있겠지, 익숙해지면 괜찮아지겠지!

다음 학습 계획

  • TDD, 스프링부트 학습

Last updated