사용자 추천 기반 학습 컨텐츠 제공 시스템 'Hello-Study-World’입니다.
이 소프트웨어는 세상에 어떤 가치를 줄 수 있나요?
우리에게 지금 당장 필요한 것이 무엇인지 고민했고, 결론은 학습과 이를 통한 성장이었습니다. 여러 요소들이 필요했지만 소프트웨어로 빠르게 제공 가능한 요소는 양질의 학습 자료였습니다.
정보의 홍수에서 질 좋은 학습 자료를 발견하는게 쉽지 않았습니다. 학습할 시간도 없는데 자료를 찾는데에 시간을 투여하는 것이 '학습의 오버헤드’라고 느꼈습니다.
같은 고민을 하는 개발자들이 힘을 합쳐 서로에게 도움을 주는 공간을 만들고 싶었습니다. 개발자 특유의 공유 문화를 활용해 먼저 걸어갔던 누군가가 무엇이, 왜 좋은지를 알려주면 좋지 않을까? 생각했습니다. 그래서 저희는 양질의 자료와 그 이유까지 빠르게 얻어가는 시스템을 만들기로 했습니다.
프로젝트를 진행하며 고민했던 요소와 왜 선택하게 됐는지?
-
1. 테스트를 Small, Medium, Large로 나눈 이유는 무엇인가요?
Spring Boot에서 제공하는 슬라이스 테스트처럼 계층별로 테스트를 격리하고, 병렬로 실행하여 개발 생산성을 향상시키려는 의도가 담겨져 있습니다. 2010년에 작성된 Google Testing Blog - Test Sizes를 참고했습니다.
현재 유지중인 테스트는 총 90여개로 평균 12~13sec의 시간을 소요합니다. 사이즈 별 전체 테스트 케이스는 (Large ~ Small) 22%, 32%, 45% 비율로 분포되며, 실행 시 소요 시간은 (같은 순서로) 5sec, 3.1sec, 6sec 입니다.
이러한 테스트 격리 환경 + 사이즈별 테스팅을 통해 `Small Test`의 경우 전체 테스트를 실행시키는 것 대비 최소 50% 이상(12sec → 6sec)의 소요시간을 감소시킬 수 있습니다.
-
2. Token 기반 로그인을 왜 선택하게 됐는지?
로그인 기능에서 필요한 '상태 보관' 전략에는 Session based
, Token based
두 가지 선택지가 존재했습니다. 두 선택지는 보안과 수평 확장성(horizontal scalability) 관점에서의 트레이드오프가 존재했습니다. 각 선택지의 로그인 상태 발급 절차는 아래와 같습니다.
Session based 방식은 서버가 사용자의 로그인 상태를 보관하며 이에 대한 제어권 역시 서버가 갖습니다. 일반적으로 쿠키를 통해 서버에 보관 중인 로그인 상태의 식별 값(Ex. Session ID)을 교환하며 Statuful 하게 로그인 상태를 유지합니다.
반면 Token based 방식은 클라이언트가 로그인 상태를 인증하기 위한 절차를 통해 서버로부터 토큰을 발행받고 이를 클라이언트가 자체적으로 유지합니다. 정상적인 사용자가 요청을 보내리라 가정하는 상호 신뢰를 기반으로 하죠.
보안
위와 같은 특성 때문에 Session based 방식은 상대적으로 보안 에 강점을 갖습니다. 서버가 상태 제어권을 갖고 있으므로 의심스러운 행동이 포착되거나, 보안 관련 위협을 인지했을 때 유지 중인 Session ID DB에서 특정 ID를 조회, 삭제하는 행위 등을 통해 곧바로 사용자의 권한을 박탈시킬 수 있습니다.
반면 Token based 방식은 제어권의 주체가 클라이언트입니다. 토큰이 전송되기 전까진 해당 토큰의 존재 여부마저 서버가 파악할 수 없죠. 따라서 사용자를 일방적으로 로그아웃시키거나, 세부 정보를 변경하는 등의 보안 관련 작업이 불가능합니다. 토큰의 전송 주체가 최초 발급한 사람과 달라져도 이를 알 수 없다는 단점도 존재합니다.
수평 확장성(horizontal scalability)
또 다른 점은 수평 확장성(horizontal scalability)
입니다. Session based 방식은 Stateful하기 때문에 웹 애플리케이션의 개수가 늘어날 때 상태 유지에 어려움을 겪습니다. 항상 동일한 로그인 상태를 유지하기 위해 우린 모든 서버에서 로그인 프로세스가 완료된 Session Id를 동기화해야 하기 때문이죠.
반면 Token based 방식은 서버에선 발급된 토큰에 대한 유효성 검사만 수행합니다. 이러한 Stateless한 특성 때문에 수평적 확장에 훨씬 수월합니다. 어느 서버로 접속하든 전달받은 토큰이 유효하다는 검증만 진행하면 끝입니다.
결론
위와 같은 고민 끝에 보안의 강점을 일부 포기하더라도, 서비스 규모 확대에 따른 서버의 수평 확장성을 보장하기 위해 Token based
방식을 선택했습니다.
-
About Architecture
이번 프로젝트의 목표 중 하나는‘의존성을 잘 관리할 수 있는 아키텍쳐’로 설계된 소프트웨어를 구축하는 것이었습니다. 실무에서 아키텍쳐 관련 실습은 어려움이 있어 사이드 프로젝트 레벨에서 실행하기로 결정했습니다.
기능과 구조 사이에선 구조가 중요하다. 구조의 ‘나름의’ 깔끔한 규칙을 유지하는 것을 통해 유지보수를 위한 맨먼스를 줄일 수 있기 때문이다.
NHN 2022 Forward의 NHN Forward 2022 - 클린 아키텍쳐 애매한 부분 정해드립니다에서 언급된 내용입니다. Jeffrey Palermo의 Onion Architercture도 구현 기술이 변경될 때마다 레거시를 재작성하는 것이 아닌 쉽게 변경할 수 있는 유연한 아키텍쳐를 갖춰야 한다고 이야기합니다.
Data access changes frequently. Historically, the industry has modified data access techniques at least every three years; therefore, we can count on needing to modify data access three years from now for any healthy, long-lived systems that’s mission-critical to the business. We often don’t keep systems up-to-date because it’s impossible to do. If coupling prevents easily upgrading parts of the system, then the business has no choice but to let the system fall behind into a state of disrepair. This is how legacy systems become stale, and eventually they are rewritten.
Martin Folwer’s Clean Architecture, Alistair Cockburn’s Ports & Adapter Architecture(Hexagonal Architecture), Jeffrey Palermo’s Onion Architecture, 중요한 것은 어떻게 부르냐가 아닙니다. 세 가지 아키텍쳐 모두 소프트웨어의 의존성이 외부에서 내부, Application Core(Domain Model)을 향해야 한다는 것이 핵심입니다. 이를 토대로 프로젝트 개발 시 의존성의 흐름을 최대 관심사로 두고 개발하려 노력했습니다.
최상단 이미지는 Jeffrey Palermo의 Onion Architecture와 저희 프로젝트 레이어입니다. Onion Architecture는 Application Core를 중심으로 변경될 수 있는 외부 레이어를 밖으로 밀어냅니다. 인터페이스를 통해 낮은 결합도를 유지하고 Dependency Injection을 통해 실제 구현체를 주입받는 형태로 설계합니다. 이를 통해 Application Core와 구현체들이 Type Dependency를 가지지 않도록 만들 수 있습니다.
현재 아키텍쳐는 Data Access(Infrastruture) 기술을 변경해도 Application Core의 수정이 일어나지 않습니다. 또한 @UseCase
,@Infrastrcuture
을 통해 Layered Architecture 내 Service Layer의 @Service
가 Application Service Layer와 Infrastructure Service Layer로 명확하게 분리되도록 작성했습니다.