안녕하세요.
IT 기업 개발자로 근무하며, 대규모 시스템을 지탱하기 위해 다양한 기술을 활용해보고 있습니다.
실무 관점의 개발 지식을 공유하고자 개설하였고, 많은 도움이 되었으면 좋겠습니다.
[문의]
Email : kukekyakya@gmail.com
Courses
Reviews
- Learning Large-Scale System Design by Building Directly with Spring Boot - Bulletin Board
- Spring Boot: Learn Large-Scale System Design by Building - Cache Strategies
- Spring Boot: Learn Large-Scale System Design by Building - Cache Strategies
- Learning Large-Scale System Design by Building Directly with Spring Boot - Bulletin Board
- Spring Boot: Learn Large-Scale System Design by Building - Cache Strategies
Posts
Q&A
커서 기반 페이지네이션 과 무한 스크롤링
KMC님, 안녕하세요! 무한 스크롤링은 두 번째부터 보통 끝쪽 id 에 데이터를 기반으로 데이터를 불러오는 방식이보통 커서 기반 페이지네이션과 큰 차이가 없는 건가요? (claude 나 ) 블로그 보면서 이것도 비슷한 개념인 거 같아서 질문 드립니다.무한 스크롤 사용성에서 특정 기준점(강의에서는 마지막 ID)으로 다음 데이터를 불러오는 것이 커서 기반 페이지네이션입니다! 동일한 의미입니다.무한 스크롤은 실제 사용자 입장에서의 용어이고, 내부적인 구현 관점에서는 커서 기반으로 동작합니다.물론, 무한 스크롤이 항상 커서 기반으로 동작해야한다는 것은 아니고, offset/limit 방식으로도 무한 스크롤링을 구현할 수는 있습니다. 다만, 무한 스크롤링이라 하면 커서 기반 구현을 일반적으로 사용하긴 하네요. 이렇게 엔티티에 인덱스를 만들어서 사용하는지 궁금합니다. 강의에서 처럼 SnowFlake도 보통 저렇게 인덱스를 만들어서 활용하는지도 궁금합니다.Snowflake는 그저 오름차순 수를 충돌없이 빠르게 생성하기 위한 알고리즘이고, 이러한 특성으로 인해 primary key로 사용될 뿐 인덱스와는 무관합니다.아무튼 엔티티에 인덱스, 연관 관계 등의 애노테이션을 질문주신 것으로 이해했는데요.그렇게 해도 되고, 안해도 됩니다. 강제될 부분도 없고 자유롭게 하면 됩니다.하지만 조금 더 실무 관점에서 본다면, 인덱스를 코드에 애노테이션으로 정의하는 경우는 잘 없습니다.물론, 위처럼 사용하는 사람도 있겠지만, 한번 사용되고 말 코드이고 DDL은 별도 스크립트 또는 문서로 관리하거나 db에 생성된걸 직접 보는게 일반적인 것 같네요.연관 관계 애노테이션도 객체 간에 강결합되면 유지보수도 어렵고, 애초에 분산 환경이라면 객체 참조 자체가 어려울 수 있어서 굳이 사용하진 않네요.저와 주변의 경우를 보면, 애초에 JPA 자체를 안쓰는 경우가 더욱 일반적이라고 생각되네요.
- 0
- 2
- 18
Q&A
섹션3에 22번째 강의에서 에러발생합니다..
MinKyu Song님, 안녕하세요! 코드가 아닌 로컬 환경에 관한 부분은, 제가 정확한 원인을 추적할 수 없다보니 직접 해결해 드리기엔 어려움이 있네요..! 이전에 RedisTestContainerSupport 처음 만들고 테스크 수행했던건 정상적으로 돌아가고 있고,SplitShardedBloomFilterRedisHandlerTest.mightContain 테스트만 실패하고 있다는 말씀이실까요? 로컬에 도커가 잘 실행되고 있는지 다시 한번 점검해보시고,구글에 Could not find a valid Docker environment 검색해보면 비슷한 사례들이 나오는데 하나씩 확인해보시겠어요?
- 0
- 4
- 30
Q&A
게시글 생성 로직에서 오류 발생시 redis 게시글 수 되돌리기
songso0412님, 안녕하세요! 강의에서는 게시글 생성과 게시글 수의 정합성을 위해 모두 rdb에 저장하고 단일한 트랜잭션으로 관리하는 전략을 취하고 있는데요,그와 별개로 redis에 게시글 수를 저장하는 상황을 말씀하시는 것일까요?rdb 트랜잭션이 커밋된 후에 redis 증가가 실패하거나,redis 증가된 후에 rdb 트랜잭션이 롤백되는 경우가 있겠네요. 정합성을 반드시 100% 완벽하게 지켜야하는지에 대해서도 고민해볼 수 있을 것 같습니다.이러한 상황이라면 트랜잭션이 롤백될 때의 예외를 캐치해서, 애플리케이션에서 redis 카운트를 다시 감소시켜주면 됩니다.물론 이렇게 하면 redis 감소 코드가 실패할 수도 있기 때문에 정합성은 완벽하게 보장되진 않습니다.하지만 오류로 인해 정합성이 깨지는 상황이 흔하게 발생하는 것도 아니고, 어떤 데이터인지에 따라서 깨지는게 문제되지 않을 수도 있습니다.후처리 보정을 넣는 전략도 겸할 수 있는 것이고요. 감지가 되었을 때, 문의가 들어왔을 때, 주기적으로 검사(샘플링 등)할 때 등 데이터의 정합성을 뒤늦게 보정 시켜줘도 시스템 운영에 크게 문제가 없을 수 있습니다.무튼 이렇게 애플리케이션에서 보상 트랜잭션을 수행해서 해결할 수 있습니다. (별도의 트랜잭션 또는 로직으로 정합성을 맞춘다는 의미입니다. 하지만 보상 트랜잭션에 대한 실패에 대한 고려도 필요하네요.) 근데 정합성이 주요하다면, rdb 트랜잭션으로 묶어내는게 가장 간단하고 확실한 해결책일 수 있습니다.정합성을 위한 다른 방법으로는, 마지막 챕터에서 소개하는 Transactional Outbox 전략과 컨슈머의 멱등 또는 중복 방지 전략을 취할 수도 있습니다.게시글 생성 트랜잭션이 완벽하게 종료되었을 때에 이벤트를 유실 없이 보내고, 컨슈머에서는 게시글 수를 증가시켜줍니다. 물론 중복 이벤트에 대한 문제가 발생할 수 있기 때문에 일정 시간 버퍼해두거나 처리한 이벤트에 대해서는 관리하는 전략을 겸해야겠네요. 어떠한 사유로 redis에 관리하시려는 의도인지 정확히 파악한건 아니지만,저라면 정합성이 필요한 데이터를 rdb/redis에 분리 저장하면서까지 인프라와 운영 복잡도를 높일 바에야 rdb 단일 트랜잭션을 이용할 것 같습니다.조회 성능에 대한 우려가 있다면(공간에 대한 우려는 오히려 없을 것이고요), rdb에 먼저 쓰기 작업을 한 뒤에 redis에 읽기용으로 따로 데이터를 캐시해둘 수도 있는 것이고요. 딱히 정답은 없고 시스템 운영 방식에 따라서 해결책도 다양할 수 있어서, 상황에 알맞게 적당한 방법을 찾아나가는게 좋을 것 같네요!
- 0
- 2
- 46
Q&A
http://localhost:8080/cache-strategy/{{cacheStrategy}}/items 호출 시 NPE 에러 문의
리나님, 안녕하세요!이번에도 잘 수강해 주셔서 감사합니다. CGLIB이 아닌 JDK Proxy(인터페이스의 파라미터 정보를 보존하지 못함)가 동작하는 것으로 보이고, 로컬 IDE 컴파일 환경에서 차이가 있는건지 싶군요..! KukeCacheKeyGenerator.genKey 메소드에서 아래 출력 결과 공유해주실 수 있을까요?System.out.println(joinPoint.getThis().getClass()); 해결 방법으로는,인텔리제이 Settings > Build, Execution, Deployment > Compiler > Java Compiler 메뉴에서,Javac Options > Additional command line parameters에다가 +parameters를 추가해보시겠어요?class 파일에 파라미터명을 보존하는 설정입니다. 위 방법이 안되면, build.gradle에 아래 설정을 추가해보시거나,tasks.withType(JavaCompile) { options.compilerArgs += ["-parameters"] } KukeCacheApplication에 @EnableAspectJAutoProxy(proxyTargetClass = true)를 추가해보시면 될 것 같습니다! 혹시 세 가지 방법 모두 안된다면 다시 말씀 부탁드립니다!
- 1
- 2
- 55
Q&A
멀티 모듈이 아닌 MSA 환경에서 common
newsungk7님, 안녕하세요! 말씀 주신 내용은 정답도 없고 구현하기 나름입니다.가장 먼저 고려해볼만한 부분은, 굳이 언어 레벨에서까지 분리할 필요성이 있을지 고민해볼 수 있을 것 같습니다.여러 프로젝트 간에 반드시 공통 코드로 빌드해야 하다면, 그냥 단일 프로젝트에서 모듈로 코드를 공유하는게 생산성이 훨씬 올라가고 관리도 편하므로, 굳이 분리할 이유도 없고 분리하지 않는게 유리한 측면이 많습니다. 특정 기능을 위한 특화된 언어 또는 프레임워크로 분리해서 만들고 싶다면, 아예 별도의 플랫폼을 만들어서 API/이벤트 등의 별도 프로토콜을 열어줄 수도 있습니다. 핵심 비즈니스 도메인은 이러한 플랫폼을 활용해서 필요한 기능을 구현하면 되는 것이고요. 이처럼 하면 API와 같은 인터페이스 레벨에서 내부 언어/프레임워크 등은 추상화되므로 문제될 부분은 없습니다. 각 언어마다 공통 기능으로 호환되는 라이브러리를 만들어서 제공할 수도 있을 것 같습니다.관련해서 배포된 것들도 이미 많이 보셨을 것 같고요.불가한 부분은 아니지만 라이브러리 개발 및 배포 비용도 필요하고, 사용처가 많다면 버전 및 호환성 등 관리가 꽤나 복잡하고 귀찮은 부분입니다. 또, 중복 코드를 항상 공통화할 필요는 없기도 합니다. 딱히 중복이 우려되지 않고 지속적으로 관리되어야 하는 코드가 아니라면, 코드 중복으로 관리하는 것도 문제되지 않는 상황이 많습니다. 만약 여러 언어/프레임워크가 모두 다를 수 있는 프로젝트에서 공통으로 동작 시키려는 코드를 동일하게 만들고 싶더라도, 언어가 다르기 때문에 각 프로젝트마다 다르게 구현될 수도 있습니다.동일한 기능으로 만들어내더라도, 애초에 이미 다른 코드입니다.내부 동작이 복잡할수록 기능에 대한 명세를 일관되게 유지하는 것도 어려워질 수 있습니다. 아무튼 현 요구사항인 단일한 게시판 서비스를 이루는 시스템이라면, 게시판이라는 상위 도메인 내에서 분류되는 게시글/댓글 하위 도메인 단위까지 별도 프로젝트로 분리를 해야 하는 것인지 필요성부터 다시 검토를 해보게 될 것 같습니다.적어도 팀 단위의 분리 또는 플랫폼화의 필요성 등이 있을 때에 프로젝트 분리를 고민해보면 좋을 것 같네요!
- 0
- 2
- 50
Q&A
2Depth 강의 도중 궁금한 점 있어요!!
박력님, 안녕하세요!잘 수강해주셔서 감사합니다. A가 논리 삭제되어있더라도, B를 삭제한다고 해서 A가 물리 삭제되진 않습니다.아직 C가 남아있기 때문입니다.지금 코드에서 동작이 그렇게 처리되고 있다는 말씀이실까요?!
- 0
- 2
- 36
Q&A
샤딩에 대해서 궁금점있습니다.
hahahl님, 안녕하세요! 일반적으로 샤딩이라 함은 수평 분할에 대한 용어로 사용되지만, 개념적으로 수직 분할이란게 없는건 아닙니다. (공식적인 용어에 대해서 묻는 것이라면, it 분야가 용어와 그에 대한 해석이 다양할 수 있고 비공식적인 용어도 흔히 사용되다보니, 그 부분에 대해선 저도 모르겠습니다.)수평(테이블로 치면 레코드 단위)으로 분산하는게 아닌, 수직(테이블로 치면 컬럼 단위)으로 분산하면 그것이 수직 분할인 것이고요.사실 1:1 테이블 관계로 정규화한다라는 개념과 딱히 다를 것 없다고 생각되기도 하네요. (물론 엄밀히 따지면 다르지만요)특별히 설명 드릴만한게 있는 부분도 아니고 용어에 대해서 깊게 짚고 넘어가야할 부분은 아니라, 크게 신경 안쓰시고 넘어가셔도 괜찮을 것 같네요!
- 0
- 2
- 68
Q&A
게시글 테스트 데이터 삽입 - @PersistenceContext 에 관하여
jack8226님, 안녕하세요! TransactionTemplate을 사용하여 트랜잭션을 따로 열고 EntityManager를 활용하는 부분은,단순히 건건이 쿼리보단 벌크가 빠르므로 테스트 데이터 삽입을 빠르게 처리하기 위한 작업이었고,그냥 JPA에서 bulk 연산을 하려면 트랜잭션 종료 시점에 한번에 flush 되니까 그렇게 처리할 수 있겠더라고요.이를 위해 TransactionTemplate으로 트랜잭션을 직접 연 것이고(같은 클래스에서 aop는 동작 안하므로), 벌크 용도로 EntityManager를 쓴 것입니다. PersistenceContext 로 em 을 가져온 이유가 있나요? 아니면 선호하시는 방식이라 채택한 방법인가요? 그리고 질문 주신 사항에 대해서도 딱히 의미를 두고 차이를 만든 부분은 아니라, 크게 신경쓰실 필요는 없을 것 같습니다..!어떠한 방향을 선호하는 것도 아니고, 그러한 세부적인 차이를 알 정도로 JPA를 공부하진 않았고 공부할 생각도 딱히 없습니다. (개인적으로 선호하는 방향은 JPA 자체를 아예 안쓰는 것이고, 실제로도 안쓰고 있습니다.)저도 질문 주신 것 보고 무슨 차이가 있었나 궁금해서 지피티한테 물어봤지만,이 답변을 그대로 전달드린다고 해서 딱히 의미가 있을 것 같지도 않고, 작성한 스크립트에서는 딱히 동작 상 차이가 있지도 않네요.그냥 JPA 학습할 때 @PersistenceContext를 썼던 기억과 JPA 관련 애노테이션이라 쓴 것이고, 단순 데이터 초기화 스크립트 용도라 별다른 의미를 두거나 동작 상의 차이를 이해하고 있던 부분은 아닙니다.또 개인적으로 추구하는 방향은, 굳이 몰라도 된다고 판단한 부분은 추상화된 영역까지 굳이 알려고 하진 않기도 합니다. 코드 의도를 파악하시는 것 너무 좋은 자세지만, 해당 부분은 저도 딱히 신경쓰고 만든 부분은 아니라 만족스러운 답변은 아닐듯하여 죄송스러운 마음이 있네요.. ㅎㅎㅎ
- 0
- 1
- 47
Q&A
댓글 테이블 설계
jjs270402님, 안녕하세요! 실무에서 외래키 제약 조건은 걸지 않는게 일반적입니다.물리적인 제약 조건을 걸면 스키마 변경에도 어려움이 생기고, 데이터 CUD 시에도 제약 조건을 검증해야 하므로 DB 부하는 더욱 커집니다.DB의 리소스는 가장 소중하게 다뤄야할 부분이고요. (결국 모든 병목/장애 지점이 DB가 될 수 있으므로)그렇기 때문에 물리적인 제약 조건은 걸지 않습니다.댓글 테이블은 논외로 보고 애초에 테이블 자체가 물리적으로 분산되어 있는 분산 DB라면, 물리적 제약 조건을 아예 설정하지 못할 수도 있습니다.그렇다고 외래키가 아예 없다는 의미는 아닙니다.논리적인 설계에서는 외래키를 설정한 것이 맞고, 물리적인 제약을 설정하지 않는다는 의미입니다.그리고 논리적인 관계에 대해서는 DB 제약 조건으로 검증하는게 아니라, 애플리케이션에서 직접 제약 조건을 검증할 수 있는 것이고요.강의에서도 이러한 방식을 취하고 있습니다!
- 0
- 2
- 39
Q&A
lockType 오류 및 카운트 체크 안 됨
안녕하세요! 위 내용만 보고는 원인 파악이 어렵네요, 혹시 스프링부트 버전이 강의와 다를까요?최신 버전에서는 restClient에서 retrieve()까지만 하면 실제 api 호출이 안될 수 있어서, retrieve().toBodilessEntity()까지 호출해보시겠어요?
- 0
- 2
- 42





