안녕하세요.
IT 서비스 대기업 개발자로 근무하며, 대규모 시스템을 지탱하기 위해 다양한 기술을 활용해보고 있습니다.
실무 관점의 개발 지식을 공유하고자 개설하였고, 많은 도움이 되었으면 좋겠습니다.
[문의]
Email : kukekyakya@gmail.com
안녕하세요. 쿠케입니다.
IT 서비스 대기업에서 백엔드 개발자로 재직 중이며, 대규모 트래픽을 지탱하는 서버 애플리케이션을 개발합니다.
현재 인프런에서 대규모 시스템 강의들을 개설 및 운영하고 있습니다.
다양한 도메인의 서비스를 개발 및 운영하고 있으며, 대규모 레거시 프로젝트 뿐만 아니라 신규 프로젝트도 여러 번 경험을 해왔습니다.
주력 기술로는 Java, Spring Boot, RDB, NoSQL, Redis, Kafka 등의 안정적이고 주요한 기술을 다루고 있습니다.
MSA, DDD, EDA 등의 방법론을 활용한 분산 시스템 아키텍처를 직접 밑바닥부터 구성 및 운영해온 경험이 있고,
알고리즘 문제 풀이 및 CS 공부도 간간히 즐겨하고 있습니다.
개발 관련하여 이것저것 궁금한 점 나누는 시간으로 만들어보고자 합니다.
설계에 대한 논의 또는 자문, 개발 방법론 관점이나 생각 공유, 구현 방식에 관한 논의, 공부 방법, 코드 리뷰, 포트폴리오 리뷰 등..
무엇이든 좋습니다.
물론, 제가 모르는 주제는 진행하지 않습니다.
신청 시에 멘토링 필요한 내용을 미리 공유 주시면 감사하겠습니다.
일정은 조율될 수 있고, 온라인 화상 회의(마이크/화면 ON) 또는 채팅으로 진행합니다.
원하는 방식 말씀 주시면 되고, 별도 문의는 프로필에 기입된 메일로 먼저 주셔도 됩니다.
감사합니다.
강의
수강평
- 스프링부트로 직접 만들면서 배우는 대규모 시스템 설계 - 게시판
- 스프링부트로 직접 만들면서 배우는 대규모 시스템 설계 - 게시판
- 스프링부트로 직접 만들면서 배우는 대규모 시스템 설계 - 게시판
- 스프링부트로 직접 만들면서 배우는 대규모 시스템 설계 - 게시판
게시글
질문&답변
카운팅 조회 시 질문
leettletree님, 안녕하세요! 말씀하신대로 게시글마다 좋아요 수(뿐만 아니라 조회 수, 댓글 수 등)도 항상 같이 조회하는게 일반적인 조회 패턴입니다.이 경우 데이터 조인을 위한 작업은 당연히 필요하게 되고요.아무래도 단일 테이블에서 조회하는 것도 아니고, 심지어 단일 DB에서 조인할 수 있는 것도 아니면서,네트워크 통신을 통한 마이크로서비스 간에 애플리케이션 조인을 해야하므로 성능에 대한 고민도 당연히 뒤따를 수 밖에 없습니다.이를 위한 성능 최적화에 대한 부분은 마지막 챕터 "게시글 조회 최적화 전략"에서 학습하게 됩니다. 해당 챕터에서 다루는 해결 전략을 간단히 언급 드리면,쿼리 요구사항을 만족하기 위한 “쿼리 모델”을 별도로 구성하는겁니다.(게시글 + 좋아요 수 + 조회 수 + 댓글 수)를 통합된 하나의 단일 모델로 만들고, 통합된 모델을 보여줘야하는 클라이언트 영역에서는 위 쿼리 모델을 응답해주면 됩니다.강의에서는 Redis(메모리)에 쿼리 모델을 관리하는 방향으로 진행합니다.이러한 데이터를 만들기 위한 구체적인 방법과 최적화 전략, 고려해야할 부분들은 강의 듣다 보면 학습하시게 될겁니다! 물론, 해결 방법이 위 전략만 있는건 아닙니다. (여기는 강의에서 다루지 않는 부분입니다.)데이터 조인하는게 많지 않다면 애플리케이션에서 조인하는 것도 그렇게 큰 성능 부담으로 다가오진 않습니다.물리적으로 가까운 또는 동일한 네트워크에 위치한다면, 네트워크 통신도 빠르게 처리 됩니다.동기적으로 여러 개의 데이터를 조인하는 작업은 병목이 생길 수도 있는데요, 이에 대한 해결책으로 멀티스레드 또는 리액티브 방식으로 병렬 조회하여 개선할 수도 있습니다. 게시글 조회할 때 마다 좋아요 서버로의 api요청이 불가피위에 대한 내용도 간단히 설명 드리면, 의존성 순환 참조 문제로 인해 위와 같은 요청 방향은 지양하는게 좋습니다. (의존성에 대한 내용은 마지막 챕터 CQRS에서 언급됩니다.)의존성 문제는 CQRS를 통해서 해결할 수도 있고, 그냥 하나의 마이크로서비스로 통합하여 패키지/모듈 수준에서 관리할 수도 있고, 클라이언트의 요청부터 각 마이크로서비스로 향하는 흐름 중간에 데이터 조인해주는 레이어를 하나 만들어도 됩니다.이러한 레이어는 클라이언트 개발자들이 서버를 구성할 수도 있고(BFF), 백엔드에서 클라이언트 개발자를 위한 레이어를 구성해줄 수도 있습니다. 또는, 사용자 기기(브라우저/앱)에서 여러번 호출해서 조인해줘도 됩니다. (어떤 영역은 늦게 뜨고, 어떤 영역은 빠르게 뜨고 등의 사용성도 종종 경험해 보셨을 것 같습니다.) 여기에서 구체적인 내용까지 다루기는 어렵지만,비용, 생산성, 팀의 상황, 학습 수준, 성능 등 고려하여 적절한 방법과 구조를 찾아서 성능 문제를 개선할 수 있습니다.
- 0
- 2
- 24
질문&답변
게시글 페이징시 게시글 수
진현님, 안녕하세요! 말씀하신대로 BoardArticleCount를 관리하는 상황이라면, PageLimitCalculator로 최적화할 필요는 없습니다.PageLimitCalculator는 전체 카운트 관리할 필요가 없는 상황에 사용할 수 있는 것이고,전체 카운트 수를 이미 관리하고 있다면 BoardArticleCount로 조회하는게 훨씬 빠르긴 하네요!
- 0
- 2
- 31
질문&답변
ArticleQueryModel 질문
현섭님, 안녕하세요! 지금 구조에서는 말씀하신대로 댓글 목록은 comment service에서 별도로 요청되어야 합니다.그리고 QueryModel을 어떻게 구성할지는 조회 요구사항마다 다르기 때문에, 정답이 있는 것도 아니고 필요하다고 판단되면 같이 묶어도 됩니다.다만, 일반적인 게시판 사용성을 보면 게시글과 댓글 목록을 하나의 QueryModel로 묶는건 적절치 않을 것 같습니다.게시글의 댓글은 무한할 수 있고, 게시글과 댓글 데이터의 라이프사이클은 다르며, Redis는 NoSQL이고 단일 Key에 대한 크기 제한이 있기 때문에, 댓글이 생성/수정/삭제될 때마다 단일한 게시글+전체댓글목록 쿼리모델을 매번 갱신하는 부담은 아주 크고 제한될 수 있습니다.댓글 목록은 페이징할 방법도 필요할 것이고요.또, 게시글 페이지에서 댓글 목록을 같이 보여주지 않는 요구사항일 수도 있습니다.(댓글 진입 버튼을 별도로 클릭하는 등)이 경우에는 게시글과 댓글 목록을 함께 조회해야 하는 요구사항도 없는데, 단일 쿼리모델에 저장된 댓글 목록으로 인해 오히려 질의 부담이 증가할 수 있습니다. 게시글과 댓글을 독립적인 도메인으로 바라보고,Client는 두 도메인에서 데이터를 요청하여 화면을 그려내면 될 뿐입니다.중간에 Client를 위해 데이터를 조합해주는 레이어를 둘 수도 있는 것이고요.(BFF 등) 이 상황에 댓글의 조회 성능도 높이고 싶다면,댓글을 위한 CQRS를 구성하면 됩니다.댓글도 게시글과 비슷한 구조로 QueryModel을 별도로 관리할 수 있는 것입니다.Client는 독립적인 도메인 게시글과 댓글로 데이터를 요청하여 화면을 그려낼 수 있게 됩니다. 정리하면, 서비스 조회 요구사항에 맞춰서 상황에 적절하고 효율적인 방법으로 구현해 나가면 되는 것입니다.강의에서 제시하는건 방법론과 다양한 구현 예시 중 하나일 뿐이고, 실서비스의 복잡한 요구사항에 맞춰서 적절한 구현을 채택하면 됩니다!
- 0
- 1
- 27
질문&답변
redis 종료시 캐싱 처리
진현님, 안녕하세요! 말씀하신대로 레디스에 데이터가 없으면, 레디스에서 데이터를 못 가져오는게 맞습니다.하지만 레디스도 가용성을 뒷받침하도록 운영할 수 있고(센티널 또는 클러스터), RDB, AOF 등의 백업 전략도 가지고 있습니다.또, 강의에서는 레디스에 데이터가 없더라도, 원본 데이터 서버에서 fetch 해온 뒤 다시 레디스에 갱신하는 전략을 취하고 있습니다.목록 데이터는 원본 데이터 서버에서 그대로 조회해오고 있지만, 신규 데이터가 생성되면서 점진적으로 레디스에 데이터가 다시 채워지게 됩니다. (이것도 필요하다면, 조회 후 갱신하는 전략 또는 미리 캐시에 데이터를 채워두는 전략을 취할 수 있습니다.) 이 부분은 redis에 데이터가 유실되지 않는다고 가정하신 뒤 코드를 작성한 것이 맞을까요??위와 같은 이유로, 레디스에 데이터가 유실되더라도 가용성을 유지할 수 있는 전략으로 코드가 작성된 것입니다.레디스 클러스터나 백업 전략은 이론만 설명하고 넘어간 부분이지만 가용성이 보장된 운영 방식을 가정한 것이고(인프라 구성에 대한 가정은 강의 소개에서 언급됨),레디스가 죽거나 데이터 유실되더라도 원본 데이터 서버에서 데이터를 조회 및 재갱신을 할 수 있기 때문입니다.
- 0
- 2
- 30
질문&답변
Select 조회를 할 때 내부 DB 흐름 질문
dohyeong254님, 안녕하세요! 클러스터드 인덱스와 세컨더리 인덱스의 자료구조는 모두 트리로 이루어져 있습니다.그리고 세컨더리 인덱스의 리프 노드는 원본 데이터에 접근할 수 있는 식별자, 즉 Primary Key를 가지고 있습니다.실제 주솟값을 가진게 아닌, 논리적으로 포인터 역할을 하는 Primary Key를 통해 클러스터드 인덱스에서 데이터를 찾을 수 있는 것입니다. (주솟값 가진건 MyISAM 스토리지 엔진이랑 혼동하신 것 같기도 하네요!) Primary Key로 클러스터드 인덱스에 접근하여 데이터를 찾아나가는 과정은,트리의 시간복잡도 O(logN)의 시간이 필요합니다!
- 0
- 2
- 23
질문&답변
MSA 아키텍쳐 DB관게
어진님, 안녕하세요!MSA 구조를 떠나서 실무에서 FK를 이용한 물리적 제약은 걸지 않는 것이 일반적입니다. (분리된 DB라면 애초에 걸지 못하기도 하고요.)참조키를 통한 논리적으로 FK 연관 관계를 설계하고, 애플리케이션에서 검증합니다.FK를 걸지 않는 이유는 성능 문제 또는 스키마 변경의 어려움 등이 있는데요, 따로 학습해 보셔도 좋을 것 같네요!현재 따로 준비된 다이어그램은 없습니다..!테이블 간 연관 관계 복잡도가 딱히 높진 않아서, ERD 다이어그램도 크게 어려움 없이 그려보실 수 있을 것 같네요!
- 0
- 2
- 34
질문&답변
CommentApiTest 중 오류가 발생합니다
jackt0506님, 안녕하세요! 올려주신건 테스트 코드(클라이언트)의 에러 로그이고, 단순히 서버에서 500 응답을 내려줬을 뿐이라 상세한 정보가 없습니다.발생한 에러에 대해 상세한 원인을 찾고 추적하시려면 서버 애플리케이션의 에러 로그를 살펴보셔야 합니다.서버 애플리케이션 에러 로그에 구체적인 정보가 있을거라, 이를 기반으로 다시 확인해보시겠어요?그래도 추적이 어려우시면, 에러 로그도 공유해주시면 저도 같이 살펴보겠습니다!
- 0
- 2
- 67
질문&답변
gradle source sets가 없을때
근찬님, 안녕하세요! 해당 부분은 제가 명확한 답변을 드리기엔 어렵네요..디렉토리 직접 만들어서 sync 하셔도 되는데, 인텔리제이 버전업은 한 번 해보시면 좋을 것 같네요!
- 0
- 2
- 35
질문&답변
인기글 컨슈머 질문
선웅님, 안녕하세요! 아주 좋은 지적입니다.마지막 "셀프 코드 리뷰"에서 언급하는 부분인데요, 원래 consumer와 api는 별도의 분리된 애플리케이션으로 구성하는게 맞습니다.강의에서는 마이크로서비스 개수를 줄이고 강의 진행 복잡도를 낮추기 위해 단일 애플리케이션에 통합한 것일 뿐입니다.그냥 해당 챕터에서 미리 설명하는게 좋았을지 싶네요. 어차피 분리된 애플리케이션으로 구성하고 독립적으로 개발 및 배포한다면,별도로 모듈을 구성한다기보단 각 애플리케이션 프로젝트에 필요한 코드만 넣으면 됩니다!
- 0
- 2
- 32
질문&답변
좋아요 수 구현 PessimisticLock1 질문
선웅님, 안녕하세요! int result = articleLikeCountRepository.increase(articleId); if (result == 0) { // 최초 요청 시에는 update 되는 레코드가 없으므로, 1로 초기화한다. // 트래픽이 순식간에 몰릴 수 있는 상황에는 유실될 수 있으므로, 게시글 생성 시점에 미리 0으로 초기화 해둘 수도 있다. articleLikeCountRepository.save( ArticleLikeCount.init(articleId, 1L) ); }위 코드에서 2개의 요청이int result = articleLikeCountRepository.increase(articleId); 위 코드를 "동시"에 호출한 상황을 가정해보겠습니다.아직 ArticleLikeCount가 생성된 적이 없다면, 두 요청은 모두 result가 0으로 응답됩니다.if (result == 0)두 요청은 위 조건이 모두 true로 들어가면서 ArticleLikeCount 1로 초기화하는 코드를 두 번 수행하게 됩니다.그러면 1개의 요청은 유실될 수 있는 것입니다!
- 0
- 2
- 44