오늘하루도 정말 정신없이 지나갔다. 어제 해결한 동시성을 처리하는 코드로직의 오류를 발견한 뒤, 나머지 기능 개발을 오늘 모두 마치기 위해 하루종일 책상 앞에서 키보드만 치다보니 금새 오후가 되었고, 해당 기능 개발 후, 실제 애플리케이션을 구동하여 테스트도 진행하고, 부하테스트도 진행해보았다. 부하테스트를 진행하며, 앞으로 개선해야할 부분들을 몇몇 스스로 생각해볼 수 있었다. 그리고 이게 간단할 것 이라고 생각하여 계쏙했는데, 원래하려던 일을 하지 못할 정도로 내가 조금 많이 헤맸다. 이 부분은 아쉽지만,내가 부족하다는 증거이니, 해당 부분을 이번 기회삼아 공부해야겠다.
특정(한정판) 상품에 대한 계정 당 1회 주문 제한 기능추가 마무리
특정(한정판) 상품에 대한 계정 당 1회 주문 제한 을 추가한다. · Issue #25 · KEEMSY/shoes-ordering-system
설명 특정(한정판) 상품을 구매하는 경우를 고려한 1회 주문 제한을 추가한다. 한정판 주문의 경우, 단 계정당 1회만 구매할 수 있다. 구매 후, 취소 시 재구매가 불가능하다. 한정판 상품 구매를
github.com
나는 이번 기능을 개발하면서, 신규 기술스택(Redis)를 사용해볼 수 있었고, 또 Issue 관리를 위한 마일스톤도 사용해볼 수 있었다. 새로운 것은 언제나 흥미롭다. 그리고 이번 기능 개발을 해보면서 기존의 내가 생각하고 해온 방식의 개선점도 많이 느낄 수 있었다.
Issue 관리를 위한 마일스톤
나는 이번에 마일스톤을 처음 사용해보았다. 마일스톤을 사용해보게 된 계기는, 개발할 이슈에 대하여, 하나의 작업으로 PR을 작성할 경우, 해당 코드를 리뷰하기가 너무 어려울 것같고, 그렇다고해서 각각의 이슈를 작성하고, 각 이슈에 대한 PR을 작성하자니, 여렇 이슈들과 뒤죽박죽되어(열린상태이든, 닫힌 상태이든) 해당 기능에 대한 작업들을 모아서 보기가 힘들 것 같았다.
물론 내가 작업할 때마다 가장 시작이된 이슈에 하나씩 추가를 해주면 되긴 하겠지만.. 이건 너무 불편하다... 그렇다고해서 하나의 PR로 해당 작업을 모두 처리하기에는 너무나도 큰 PR이 될 것 같아 고민이 되었고, 이 문제를 해결할 방법을 찾다 마일스톤을 알게되었다.
마일스톤은 관련 작업을 하나의 그룹으로 만들어 사용할 수 있는 것 같다. 덕분에 내가 해결하고 싶던 문제는 모두 해결하면서 얻고싶은 부분은 모두 얻을 수 있었다. 다만, 하나의 작업으로 PR을 작성할 경우, 해당 코드를 리뷰하기가 너무 어려울 것 같다는 생각이 처음부터 들지 않았기에.. 이번에는 약간(?)의 실수가 많다..
작업 브랜치의 베이스 브랜치를 내가 명확하게 하지않았다. 그렇다보니, 베이스 브랜치의 작업이 PR을 작성할 때, 우르르 생기기도하였고.. 하나의 브랜치로 이곳(Product) 저곳(Order) 이어서 작업하다보니, 내역의 중복이 생기기도 했다. 그리고 작업 내역을 반영하는데 있어, PR 및 브랜치 전략 에 대한 고민도 많이 생각하게 되었다.
PR 및 브랜치 전략에 대한 고민
dev(QA), main(Production), local(feature) 의 형태가 나는 가장 일반적인 브랜치 전략의 형태라고 생각하는데, 신발 주문 시스템의 브랜치 전략은 현재 일반적인(?) 브랜치 전략의 모습은 아니다. 굳이 이야기를 한다면, dev(QA)가 없는 main 과 local 만 존재하는 형태라고 이야기 할 수 있을 것 같다.
더 정확히 나는 브랜치 전략을 고민할 때, 어떻게하면 특정 기능에 대하여 병렬적으로 개발이 가능할까? 를 고민하다보니 내부 아키텍처 구조에 따라 브랜치를 나눴다. 내가 설계한 내부 아키텍처는 adapter, domain 으로 나눠지고 있으며, adapter 는 또 다시 in, out adapter 로 나누어지며, domain 에서는 core 와 application 으로 나뉘어 진다. 따라서 나는 adapter, domain, application 으로 브랜치가 구성된다.
각 계층은 클린아키텍처(Ports and Adapters) 를 따르도록 설계한 것인데, 관련된 추가적인 내용은 다음 글을보면..! 이해는 할 수있다!
[ 아키텍처 ] 클린아키텍처의 구성과 패키지 구조
포스팅에서의 클린아키텍처는 Ports and Adapters 아키텍처, 헥사고날 아키텍처(육각형 아키텍처) 모두 같은 것을 의미한다. 이번 포스팅에서는 지난 포스팅에서이야기한 클린 아키텍처(Ports and Adapte
sykeem.tistory.com
근데 이렇게 나누어지다보니, 정기적으로 작업 내용을 해당 브랜치들에 개발된 내용(Main브랜치의 코드)를 반영해야했다. 이것은 당연하다. 그리고 이 작업은 비용이 많이 들지 않을 것 이라고 생각했다. 하지만 도메인이 늘어나면 늘어날 수록 생각보다 쉽지 않은 작업임을 알게되었다. 머릿 속 생각은 이것이 올바른 길인 것 같은데.. 비용을 생각해보면 또 내가 잘못행각하는 것은 아닌가.. 하는 고민을 하게 되었다. 아니면 혼자서 이것들을 관리하려다보니 내가 힘들어하는건가..? 싶기도하다.
가장 최고는 실제 협업에서는 어떤식으로 관리하는지 보고 배우는건데.. 빨리 취업해야겠다!!
신규 기술스택(Redis)의 사용
나는 이번 기능 개발을 위해 Redis 를 사용했다. 근데 이제보니 왜 Redis 를 통해 해당 기능을 구현하게 되었는지가 누락된 것 같다. 이에 대한 고민은 많이하고서 선택이 된 것인데(...물론 Redis를 사용 한다 결론이 난것은 무진장 빨리된 것은 비밀..이다)
나는 신발 주문 시스템을 처음 상상(설계)할 때, 제일 하고싶은 것이 바로 지금 부분이었다. 한정판 신발 주문, 선착순 신발 주문, 주문 제한 항목 에 대한 기능 구현이 핵심이자 내 목표였다. 선착순 구매, 한정판 신발 주문 에 대한 특성을 고민하면서 아키텍처를 분석하고 이에 대한 설계를 했었다.
참고 Wiki: 신발주문 시스템 분석 - 아키텍처
신발 주문 시스템 분석 아키텍처
shoes-ordering-system. Contribute to KEEMSY/shoes-ordering-system development by creating an account on GitHub.
github.com
나는 처음에는 당연하게 MySQL을 사용하여 이 문제를 해결하고자 하였다. 주문이 존재하는지 확인하고, 없으면 주문을 생성하는 방식을 생각했었다. 하지만 이것으로 대규모 트래픽을 감당하기에는 쉽지 않다.
그 이유로 첫번째로 성능 문제가 발생할 수 있다. 한정판 신발 주문 및 선착순 구매에 대한 경험을 떠올려보면 진짜 악성유저(?)가 많다. 한 유저가 엄청난 요청을 보내기도하는데 이에 대해 동시성 문제를 고민도 해야한다. 다른 문제들도 많겠지만, 이 동시성 문제만 생각해보더라도 아주 끔찍한 상황이 상상된다.
동시성 문제를 해결하기 위한 가장 간편한 해결책인 Synchronized 를 통해 동시성 문제(Race Condition, 경쟁상태)를 해결할 수 있다. 하지만 Synchronized 는 수평 확장시에는 또 다시 경쟁상태 문제가 발생한 다는 문제점이 존재한다.
그래서 나는 이곳에 CQRS 를 고려했었다. CQRS(Command and Query Responsibility Segregation) 을 통해 커맨드(수정)과 쿼리(조회)를 구분하고자하였다. 그리고 MySQL의 master-slave 를 적용해야겠다! 생각을 했었다.
하지만 이것들로 해당 문제를 해결할 수 있을까? 아니다. 늘어나는 요청에 대하여, 수평확장을 하지 않는다면, 결국 언젠가는 터지게될것이다. 수직확장에는 늘리는데는 한계가 있다. 내가 처음에 고민한 구조도 어느정도 트래픽을 감당해 낼 수 있겠지만, 결국에는 서버 혹은 DB가 장애가 나고 말것이다. 즉 두번째 문제 가용성의 문제가 발생한다.
사실 가용성에 대한 부분은 어떤 설계라도 결국에는 100%를 달성할 수는 없겠지만, 99.9999% 와 같이 100%와 가깝게 설계를 할 수 있을 것이다. 그렇다면 어떻게하면 가용성을 100%에 가깝게 할 수 있을까? 그 답은 정해지지 않았지만, 나는 리소스를 최대한 효율적으로 배분하여, 지킬 수 없는 부분(가용성에 영향을 줄 수 있는 큰 부분)을 최대한 지킬 수 있도록 보호(?) 하는 것이라고 생각한다.
나는 Redis에 대한 경험이 없었다. 단순 메모리 디비로 조회 때 주로 사용하며, 쿼리로 매번 조회하기에 부담이 되거나, 자주 조회하는 데이터를 캐싱하는 정도로만 사용한다 생각하고있었다. 그리고 시간이 지나 지금 기능을 개발할 때가 되었을 때, 나는 인프런의 강의 하나를 보게되었고, 이것이구나! 라는 생각을 하게되었다.
Redis 를 활용하면, 동기처리에 대한 고민을 할 필요없어졌다. 그리고 수평확장 시에도 동기 처리에 대한 고민을 하지 않아도 된다. 왜냐하면, Redis 는 싱글 스레드 기반이기 때문이다. 분산된 요청이 Redis 한곳으로 모여 결국 데이터의 무결성을 보장할 수 있다.
여담으로 MySQL은 멀티스레드 기반이라고 한다. MySQL로 해결을 하기 위해서는 반드시 Locking 전략을 고민했어야 할 것이다..
Redis 를 활용한 동시성 테스트 진행
나는 Redis를 활용해서 개발을 시작했다. 첫날에는 동시성 테스트를 작성하지 깜빡하였었다. 다음날이되어 동시성 테스트를 진행했는데, 자꾸만 테스트가 실패했다. 해당 로직에 문제가 존재했던 것이다.
나는 1000번의 요청을 동시에 보낸다는 상황을 만들어 테스트 했다. 그런데 테스트 결과는 실패했다. 처음에는 내 코드의 문제를 파악하지 못했다.
내가 작성한 로직의 문제점은 조회를 통한 값이 존재하는지 확인하는 것이 문제였다. 존재여부를 확인하는 것은 이미지처럼 문제가 발생할 수 있을음 알 수 있었다.
하지만 이를 어떻게하면 해결할 수 있을까? 에 대한 답은 쉽게 나오지 않았다. 나는 관련해서 redis 활용 사례들을 찾아보다 한가지 방법(?)이 생각이 났고, 지난날 보았던 강의 또한 다시 돌려보면서 이 문제를 해결 할 수 있었다.
redisTemplate 을 사용하여, Set 에 값을 저장할 때, 값이 중복이되면 0을 반환하고, 정상적으로 등록되면 1을 반환한다. 하지만 나는 이를 무시하고 반환 값을 지정하지않았었다.
이 문제의 원인을 스스로 생각해보았는데, 이전에 Redis 를 공부하고, 보다 편리하게 사용하기 위해 개발한 RedisService 때문이라 생각이 들었다. 아니 더 정확히는 내 redis에 대한 경험 부족때문이다..
이런 시행착오를 거쳐 로직은 다음과 같이 개선되었다.
이를 통해 내가 하고자 하는 것을 분명하게 할 수 있게되었고, 이후 이 클래스를 사용하는 다른 로직에서는 큰 어려움 없이 개발을 마칠 수 있었다.
인프라 구축, yml 분리의 필요성
해당 기능 개발 이후 나는 jmeter 를 활용한 부하테스트를 진행해보았다. 유저수(Thread) 는 1000 으로 10번 요청하는 시나리오로 다음 요청을 동시에 처리해보았다.
근데 결과가 놀라웠다.
이정도까지 나올 수 있을거라는 생각은 못했는데, 처리량이 너무 잘나와서(?) 당황스러웠다. 분명 이정도로 나올 수 가없을 것같은데 말이다.. 아마도 내 생각에는 이것이 로컬환경에서 실행되어서 성능이 잘 나온 것이 아닐까 싶다. 그래서 바로 컨테이너화를 시작하기 시작했는데, 정말 어렵지 않을 것이라 생각했던 부분들이 자꾸만 나를 괴롭힌다...(사실 정확히는 내가 부족해서 문제가 발생하는건데.. ㅎㅎ)
무튼 컨테이너 환경의 테스트 환경을 구축하기 위해 dockerfile 을 작성하고, 각 컨테이너와의 네트워크 연결을 위해 docker-compose 를 활용해서 환경을 구축하기 시작하고있다. 진작에 했어야하는데.. CD를 생각하지 않다보니..(핑계)이를 이제서야 하게 되는 것 같다.
yml 분리의 필요성
그런데 설정을 하는데 application.yml이 고민이 되었다. 로컬 환경에서는 사용하는 주소를 localhost 를 사용하지만, 컨테이너 환경에서는 해당 컨테이너 이름을 사용하기에.. yml 설정에 불편함을 느끼게되었다. 그래서 이를 위해 환경에 따른 yml 파일을 나눠보려고한다. 일단은 테스트로 컨테이너로 모든 환경을 구축 한 뒤, 해당 설정을 위한 yml 파일을 생성해야겠다.
오늘 개발 및 부하 테스트 이후, 컨테이너로 환경을 구축하는데 시간을 생각보다 많이 사용했다. 이 과정에서 내 생각과는 조금 많이 다르게 흘러가서 조금 힘들었는데, 내일은 이 막힌 부분들이 잘 해결이 되었으면 좋겠다.
아참 그리고 하마터면 못받을뻔한 생일선물이 도착했다. 책 제목이, 사진이 정말 시선강탈인데 아쉽게도 아직 읽어보지는 못했다.
내일은 조금이라도 읽어봤으면 좋겠다.
기존에는 이력서 출력만 잘하면 되는부분이었는데, 이제는 새롭게 개발된 내용을 추가해서 다시 조정해야할것같다. 아직 문서도 다 정리못했지만.. 문서도 어서정리하고 기존 문서도 다듬고.. 면접을위해 가보자 !!!!!!!
'회고 > TIL' 카테고리의 다른 글
한주 마무리, 동적쿼리 개발, Optional 에 관하여 (0) | 2023.09.17 |
---|---|
이력서 지원 시작, 독서: 소프트웨어 가치와 비용, 기존 이슈 작업 시작 및 신규 이슈 (0) | 2023.09.13 |
TestContainers 사용을 통한 Redis 테스트 및 기능 개발 (0) | 2023.09.08 |
독서: 육각형 개발자, 하루종일 이력서 수정.. 또 수정.. (0) | 2023.09.06 |
사용 DB 변경과 JPA, 독서 (0) | 2023.09.04 |