묻고 답해요
160만명의 커뮤니티!! 함께 토론해봐요.
인프런 TOP Writers
-
해결됨토비의 클린 스프링 - 도메인 모델 패턴과 헥사고날 아키텍처 Part 1
프로필 주소(profile_address) 제거 시 Unique 위반에 관한 질문
안녕하세요 토비님! 항상 강의를 통해 새로운 시야를 얻는 것 같아 감사하게 듣고 있습니다!강의를 수강하던 중 궁금한 점이 하나 생겨 질문 드립니다.우선 현재 Section7, 39강까지 수강한 상태입니다. 도메인 규칙 상으로 프로필 주소가 제거 가능하다고 정해졌고, 이는 코드로 상세히 표현되었다고 생각했습니다.하지만 "프로필 주소는 제거할 수 있다"라는 규칙을 적용해 memberA가 프로필 주소를 빈 문자열 형태로 변경한다면, memberB의 경우 동일하게 빈 문자열을 저장할 수 없으니 프로필 주소를 제거할 수 없는 것 아닌가라는 생각이 들었습니다.그래서 기존 Github에 공개된 코드 테스트에서 실험을 진행했습니다. (제 코드는 차이가 있을 수 있어서 공개된 자료를 이용했습니다.) MemberRegisterTest#updateInfoFail 에 아래와 같이 테스트를 추가해 보니entitymanager.flush() 부분에서 Unique index or primary key violation 이 발생했습니다. 제 생각에는 도메인 규칙을 변경하거나, profile_address의 값이 빈 문자열일 경우 NULL을 저장하도록 로직을 변경해야한다고 생각합니다.혹시 제가 놓친 부분이 있는지, 토비님의 의견은 어떠신지 궁금합니다. 항상 감사하게 수강하고 있습니다!
-
미해결토비의 클린 스프링 - 도메인 모델 패턴과 헥사고날 아키텍처 Part 1
계층형 아키텍처와 헥사고날 아키텍처는 정말 본질적으로 많이 다른 것일까?
최근에 제가 고민했던 부분의 토비님의 의견이 궁금해서 질문드립니다. “계층형 아키텍처와 헥사고날 아키텍처는 정말 본질적으로 다른 걸까?” 예를 들어, 계층형 아키텍처에서도 인터페이스를 통해 상위 계층이 하위 계층을 의존하도록 설계하면 DIP(Dependency Inversion Principle)를 지킬 수 있습니다. 그렇게 하면 헥사고날 아키텍처가 지향하는 의존성 역전과 사실상 동일한 구조가 만들어지지 않을까요? 그렇다면 DIP를 잘 구현한 계층형 아키텍처는 헥사고날 아키텍처와 다르지 않다고도 볼 수 있을 것 같습니다. 이 생각대로라면, 우리가 그동안 “계층형 아키텍처”라고 부르며 개발하던 많은 구조들이 사실상 헥사고날 아키텍처였던 것 아닌가? 라는 생각도 들었습니다. 만약 두 아키텍처가 여전히 다르다고 본다면, 그 차이는 폴더 구조나 패키지 구성 방식처럼 물리적인 형태에서 오는 걸까요? 하지만 두 아키텍처 모두 논리적이고 추상적인 설계 철학을 이야기하는 것인데, 물리적 구조로만 구분하는 건 이상하다고 느껴집니다. 결국 저는, 잘 설계된 아키텍처라면 헥사고날이든 계층형이든 최종 목표는 동일하다, 라는 생각이 들었습니다.즉, SRP(Single Responsibility Principle) 를 지키고, 외부 기술의 변화가 도메인에 영향을 주지 않아야 하며 (헥사고날),persistence 계층이 바뀌더라도 핵심 비즈니스 로직은 변하지 않아야 한다 (계층형),는 점에서 둘의 지향점은 같다고 느낍니다. 혹시 제가 놓치고 있는 중요한 관점이 있을까요?토비님께서는 이 두 아키텍처를 어떻게 구분하시고, 어떤 기준을 중요하게 보시는지 궁금합니다.
-
미해결토비의 클린 스프링 - 도메인 모델 패턴과 헥사고날 아키텍처 Part 1
개인적인 호기심 질문인데요
도메인에 "속성과 행위가 모두 포함"되어야하는데 그러면 만약에 "행위" 자체가 "외부 의존"을 가져야만 하는 경우에는 이런 것은 어떻게 만드는 것이 좋을까요?
-
미해결토비의 클린 스프링 - 도메인 모델 패턴과 헥사고날 아키텍처 Part 1
@TestConfiguration 관련 설명과 실제 동작이 다른 부분이 있는 것 같습니다.
안녕하세요 토비님! 좋은 강의 정말 감사드립니다.@TestConfiguration 에 대한 설명을 해주신 부분 중에서 테스트 결과와 다른 부분이 있는 것 같아 확인차 질문 드립니다. <28. 회원 애플리케이션 서비스 테스트 (2)> 의 11:38에서 "@TestConfiguration 에 어떤 Bean을 정의하면 이게 우선이 되어 돌아갑니다." 라는 말씀을 해주셨는데요.해당 말씀이 맞는지 테스트해 보기 위해, 현재의 강의 예제 코드에서 @TestConfiguration 이 붙은 클래스의 PasswordEncoder를 정의한 Bean 메서드의 이름을 testPasswordEncoder로 변경하고 테스트를 돌려 보니 아래와 같은 오류가 발생하였습니다.(EmailSender의 경우 DummyEmailSender에 @FallBack 이 붙어 있어서 @TestConfiguration 의 EmailSender Bean 메서드의 이름을 다르게 구성하더라도 테스트는 정상적으로 동작합니다.) @TestConfiguration 이 정의되었다고 해서 우선순위로 동작하지는 않는 것으로 보이고,기존 Bean의 적용 우선순위에 따라 @TestConfiguration 에서 정의한 메서드 이름과, 이를 사용하는 곳의 필드 이름이 동일한 경우 해당 Bean을 찾아서 동작하는 것으로 보였습니다. 혹시 제가 잘못 이해하고 있거나 보완이 필요한 부분이 있다면 편히 말씀 부탁드립니다. 확인 부탁드립니다.감사합니다 :)
-
미해결토비의 클린 스프링 - 도메인 모델 패턴과 헥사고날 아키텍처 Part 1
MemberRegisterRequest 의 검증 방식으로 @Valid 와 초기화 스크립트의 차이가 무엇인가요?
안녕하세요, 토비님! 훌륭한 강의 너무너무 잘 듣고 있습니다. 토비님의 고견 덕에 제 개발 세계관을 정리하는데 도움이 많이 되고 있습니다. 감사합니다. 강의의 데이터 검증 파트에서 질문이 있습니다.MemberRegisterRequest 의 검증 방법으로 jakarta.validation 을 사용하도록 설명해주셨는데요.(1) MemberRegisterRequest 의 생성자 또는 팩토리 매서드에서 값을 검증하는 방식과 validation 어노테이션을 이용하는 방식을 어떻게 구분해서 쓰는지? 와 (2) validation 어노테이션이 활용되는 영역의 범위가 궁금합니다.저는 코틀린 스프링부트로 강의를 따라가고 있어서 코틀린 기준으로 예시를 들면, 보통 저렇게 도메인 레이어에서 비즈니스 로직에 직접 사용되는 데이터를 검증하는 경우에는 코틀린의 init 블록 내에 비즈니스 관련 데이터 검증 로직을 넣어서 해당 클래스 객체의 데이터 정합성이 항상 보장되게 해왔는데요. (게다가 도메인 레이어의 클래스다보니 더욱 타 기술 의존 없이 검증하는 게 좋다고 생각해서 init 블록을 애용해왔습니다.)반대로, Valid 는 외부로부터 입력 받은 데이터들의 아주아주 기본적인 데이터 타입 검증 용도(Nullable, 숫자, 이메일, 공백 여부 등) 정도로만 사용해왔습니다. 애초에 제공해주는 어노테이션의 기능이 비즈니스 의미를 담기엔 턱없이 부족해서, "데이터가 비즈니스 의미상으로는 틀릴 수 있어도, 타입 자체는 맞아" 정도만 보장해주는 용도라고 느꼈습니다.그래서 강의에서 어플리케이션 서비스의 파라미터나 도메인 객체의 상태를 검증하는 용도로 사용하시는 모습이 조금 낯설게 느껴졌습니다.어플리케이션 서비스 파라미터에 들어있는 데이터는 이미 컨트롤러에서 기본 검증은 끝난 데이터들이라고 생각해서요.게다가 Valid 를 어플리케이션 서비스에서도 쓰기 시작하면, 컨트롤러와 서비스에서 중복 검사를 하게 될 것 같습니다.이런 점들에 대해서 어떻게 생각하시고 어떻게 구분하시는지 그 기준이 궁금합니다.Q&A 의 다른 질문들에 대한 토비님의 답변들을 보면서도 많이 배우고 있습니다. 고견 감사합니다! 🙂
-
미해결토비의 클린 스프링 - 도메인 모델 패턴과 헥사고날 아키텍처 Part 1
만들면서 배우는 클린 아키텍처 관련된 질문
강의에서도 잠깐 언급되지만, 전체적인 강의 내용이 만들면서 배우는 클린 아키텍처의 저자가 주장하는 내용과는 지향하는 바는 같지만 그 구현에 있어서 어느정도 거리가 있다고 느껴지는데요..!"만들면서 배우는 클린 아키텍처" 이 책을 읽으면서도 저자가 주장하는 내용이 좋은 아키텍처라는 생각이 들었었고, 그 지름길에 대해서도 많은 생각이 들었습니다.그러다보니 강의에서 언급해주시는 일부 내용들은 이 지름길을 사용했다는 것으로도 저한테는 느껴지기도 하는데요. 하지만 또 다른 측면으로 토비 선생님께서 강의에서 말씀하시는 내용들이 매우 타당하고 이것 또한 매우 클린하며 헥사고날 아키텍처의 장점을 매우 잘 살리고 있다는 생각도 들었습니다.제가 궁금한 건 "만들면서 배우는 클린 아키텍처" 에서 저자가 주장하는 내용들에 대해 토비 선생님께서는 어떻게 생각하고 계시는지 궁금합니다!회사에서 많은 시니어 분들이 헥사고날과 관련해서는 저자마다 주장하는 내용이 다른 경우가 많으니 최대한 많이 접하고 스스로 기준을 삼는 것이 좋다라고 조언을 주시기도 하셔서 더욱 궁금하네요 ㅎㅎ:)!
-
미해결토비의 클린 스프링 - 도메인 모델 패턴과 헥사고날 아키텍처 Part 1
안녕하세요 토비님
질문이 있어 남깁니다.30. 회원 애플리케이션 기능 추가 강의 중 Activate 메서드를 작성하면서 설명해주신 Spring Data Jpa 사용시 save를 사용해야 한다고 공식문서에 나와있다고 하셨는데 해당 문서에 대한 링크를 알수있을까요?save를 안티패턴이라고도 설명을하고 불필요한 오버헤드 발생에 대해서는 어떻게 생각하시나요? 강의의 내용 처럼 JpaRepository가 아닌 Repository를 사용 하는경우에는 필수적으로 save를 해야 하나 JpaRepository의 경우는 Jpa 자체에서는 save라는 것이 없기 때문에 새로운 엔티티를 생성할때만 사용을하고 업데이트의 경우는 생략을 해야하는 것일까요?
-
미해결토비의 클린 스프링 - 도메인 모델 패턴과 헥사고날 아키텍처 Part 1
JPA모델과 도메인모델 분리가 필요한 사례
안녕하세요 🙂"도메인 모델을 직렬화 했다가, RDB에 저장했다가" 하는 경우도 JPA와 도메인모델 분리가 필요한 경우라고 생각되는데 어떻게 생각하시나요?도메인모델이 생성되었을때 영구보관이 필요한게 아니라, 어느정도 상태머신이 진행된 후 영구 보관이 필요하여 그전에는 레디스나 다이나모 같은 저장소에 보관하다가, 이후에 RDB에 영구보관을 하는 경우가 좀 더 자세한 예시일 것 같아요. 이 경우 JPA에서 DB 성능등을 이슈로 양방향맵핑을 하는 경우 순환참조로 인한 직렬화 이슈가 생기기 때문에 어떻게 해결할 수 있을지 고민하다가 이때 모델 분리를 선택한 경험이 있습니다. -- 무조건적인 지향을 하는게 아니라 필요에 따라 기술을 선택할 수 있게 강의를 진행해주시는 점 너무 많이 배우고 있습니다. 감사합니다.
-
미해결토비의 클린 스프링 - 도메인 모델 패턴과 헥사고날 아키텍처 Part 1
MemberRegisterRequest 에 대해서
토비님, 항상 좋은 강의 감사드립니다. 강의 내용을 학습하던 중 궁금한 점이 생겨서 질문드립니다!강의 코드에서 MemberRegisterRequest가 domain 패키지 안에 직접 정의되어 있고, 이 동일한 객체를 adapter 계층의 컨트롤러에서 @RequestBody로 직접 받는 것을 확인했습니다.제가 접해온 일반적인 계층형 아키텍처에서는, 웹 계층을 위한 DTO를 별도로 두고 서비스 계층에서 이를 도메인 객체로 변환하여 도메인 계층이 웹 DTO에 의존하지 않도록 분리하는 방식을 주로 사용했습니다.그래서 강의에서 보여주신 설계 방식에 대해 궁금한 점이 두 가지 있습니다.이처럼 요청(Request) 자체를 도메인의 일부로 보고 domain 패키지에 포함시키는 설계가 갖는 이점은 무엇인지 궁금합니다.이러한 설계가 계층 간의 결합도를 높일 수 있다는 우려에 대해서는 어떻게 생각하시는지, 그리고 어떤 상황에서 이러한 실용적인 접근이 더 효과적이라고 판단하시는지, 토비님의 설계 철학이나 기준에 대해 여쭙고 싶습니다.감사합니다.
-
해결됨토비의 클린 스프링 - 도메인 모델 패턴과 헥사고날 아키텍처 Part 1
안녕하세요 토비님 개인적인 질문이 있습니다.
강의와는 관련이 없는데, 개인적인 질문이 있습니다.저는 자바 스프링 신입 개발자를 준비하고 있는 학생입니다.지금까지는 MVC 패턴만 사용하고, 모놀리틱 아키텍처를 사용해서 배포를 진행하고 프로젝트를 해왔습니다. 제가 알기로는 학습적으로나 포트폴리오적으로나 필요성을 느껴서 하는 공부가 제일 좋다고 들었습니다. 근데, 최근에는 어떤 필요성을 느끼지 못하면서 대규모 시스템 강의,헥사고날 아키텍처가 중요하다고 하니 강의 등을 듣고 있습니다. 왜냐하면, 본격적인 취업은 내년이고 시간이 좀 남았습니다. 그래서 해당 강의들을 들어두면 언젠가 개인 프로젝트나 현업에서 사용할 수 있지 않을까하고 듣고있습니다만, 제가 사용했던 MVC 패턴의 장단점 등 기본적인 것들도 알지 못하는 상태에서 계속 진도 나가듯이 이런 저런 강의를 듣고 하는게 괜찮을까요? 즉, 아직 기본도 잘 모르면서 계속 새로운 걸 배우는 과정들이 괜찮을까하는 걱정이 드네요. 하지만, 한편으로는 CS지식이 너무 방대해서 기초를 다 잡아두고 다음 단계로 넘어간다는 것도 솔직히 엄두가 안납니다. 그래서 우선은 쭉 이것저것 배워두고 나중에 필요하면 다시 찾아보면서 공부하면 되지 않을까 싶은데, 토비님은 어떤 방향이 더 괜찮다고 생각하시나요??
-
미해결토비의 클린 스프링 - 도메인 모델 패턴과 헥사고날 아키텍처 Part 1
영상 편집에 오류가 있는것 같습니다.
3:10 MemberRegisterResponse 생성3:23 MemberRegisterResponse 생성같은 과정이 반복되는데 편집이 잘 못 된것 같습니다.
-
미해결토비의 클린 스프링 - 도메인 모델 패턴과 헥사고날 아키텍처 Part 1
MemberInfoUpdateRequest, MemberRegisterRequest의 패키지 위치
학습중에 MemberInfoUpdateRequest, MemberRegisterRequest와 같은 객체들은 어댑터에서도 사용하고, 애플리케이션에서도 사용하고, 도메인 내부로직에도 사용하는데 도메인 패키지 내에 위치하는게 맞는지 의문이 들어서 질문드립니다!
-
미해결토비의 클린 스프링 - 도메인 모델 패턴과 헥사고날 아키텍처 Part 1
MemberRegister가 Member 엔티티의 C/U/D 작업을 모두 담당하나요?
안녕하세요, 토비님. 현재 MemberRegister 인터페이스는 register 뿐만 아니라 활성화/비활성화, 정보 업데이트 등의 오퍼레이션을 제공하고 있는데요,해당 인터페이스에 작성된 주석은 회원의 등록과 관련된 기능을 제공한다여서 'register를 제외한 오퍼레이션은 다른 인터페이스에 위치해야 하지 않나?' 하는 생각이 듭니다.혹은 회원 등록 외 오퍼레이션이 해당 인터페이스에 존재하는게 의도하신 바지만, 주석 내용이 수정되지 않은 걸까요? 궁금한 점을 요약해서 정리하자면, xxxFinder는 조회와 관련된 오퍼레이션을, xxxRegister는 생성/수정/삭제와 관련된 오퍼레이션을 가지는 걸까요? 완강 후 회사에서 강의로 알려주신 것들을 이것저것 적용해보며 재밌게 일하고 있습니다, 정말 감사드립니다.
-
해결됨토비의 클린 스프링 - 도메인 모델 패턴과 헥사고날 아키텍처 Part 1
같은 계층에서의 의존성 관리 질문
안녕하세요 Toby!먼저 좋은 강의 올려주셔서 감사 말씀 드립니다.열심히 강의를 듣던 와중에 궁금한 점이 있어서 질문드립니다. 회원 애플리케이션 기능 추가 강의에서 보면,MemberModifyService 클래스에서 MemberFinder 빈을 주입받아 사용하도록 구현해주셨습니다. 관련해서 같은 application 계층에서 서로 DI 받는 구조로 구성하게 될 경우, 발생하는 순환 참조와 같은 문제점들에 대해서는 어떻게 관리하는게 좋을까요? 그리고 그런 문제점이 발생하지 않도록 예방하기 위해서는 어떤 방법이 있을까요?
-
미해결토비의 클린 스프링 - 도메인 모델 패턴과 헥사고날 아키텍처 Part 1
DB 설정 문제
안녕하세요 프로젝트 초기 설정을 토비님 강의 진행대로 따라하고 있는 중입니다.제가 docker 는 잘 몰라서 강의 자료에 있는 Rancher Desktop 을 그냥 설치했구요서버 구동하기 전에 먼저 켜고 토비님 강의 순서 대로 진행했습니다 compose.yaml 의 내용을 수정하기 전에는 오류없이 잘 되는데 토비님이 작성하신 대로 수정을 해서 서버를 구동하면 계속 오류가 발생하고 있습니다 제 디비에 문제가 있나 싶어서 mysql를 완전 삭제하고 재설치 까지 해서 다시 해봐도 이전과 계속 같은 오류가 발생하는데 ai 를 통해서 해결해보려고 해도 해결을 못하고 있습니다 혹시 확인해보시고 알려주셨으면 합니다
-
미해결토비의 클린 스프링 - 도메인 모델 패턴과 헥사고날 아키텍처 Part 1
안녕하세요. 토비님! 도메인의 다양한 업데이트 요구사항을 Web API 계층에서 어떻게 다뤄야 할까요?
안녕하세요, 토비님. 강의를 들으며 많은 인사이트를 얻고 있습니다.강의를 완강한 후에도 내면적으로 정리되지 않은 부분이 있어 조심스럽게 질문을 드리게 되었습니다.생성과 관련된 설계는 강의에서 잘 이해가 되었지만, 업데이트(update)와 관련된 내용은 직접적으로 다뤄지지 않아 고민이 생겼습니다. 특히, 제 고민은 다음과 같습니다."도메인의 비즈니스 규칙이 Web API 설계에 어느 정도까지 직접적으로 드러나야 하는가?"현재 도메인 로직에서는 사용자의 여러 정보를 변경할 수 있는 비즈니스 규칙이 존재합니다. 예를 들어:비밀번호 변경 기타 세부정보 변경비즈니스적으로는 각각의 규칙이 잘 정의되어 있고, 각각의 변경 로직도 Member 객체 내에 명확히 메서드로 존재합니다.여기서, 이러한 비즈니스에 대해서 API에 어떻게 노출시켜야 하는가에 대해서 두 가지 선택지가 고려됩니다.1. 비즈니스 정의를 역할 별로 구성한다.POST /api/v1/members/{id}/change-password POST /api/v1/members/{id}/change-nickname생각이 나는 장단점은 다음과 같습니다.장점: 비즈니스에 따라 API를 관리하여 클라이언트가 이해하기 용이합니다.단점: 수정 가능한 필드가 많아질수록 API의 개수가 증가하며, 유지보수가 어려워질 수 있고, Restful 규칙에 위배됩니다.2. 하나의 update API로 통합한다.PATCH /api/v1/members/{id} { "password": "originalPassword123!", // nullable "detailRequest": { // nullable "email": "user@example.com", "nickname": "nickname123", "password": "newPassword456!" } }장점: API가 간결하여 확장이 용이하며, 클라이언트는 필요한 값만 상황에 따라 요청하면 됩니다.단점: API가 비즈니스 책임에 명확하지 않을 수 있습니다.결론적인 질문은 다음과 같이 정리 할 수 있을 것 같습니다.비즈니스 로직이 도메인 레이어에 잘 분리되어 있는 경우, API 계층에서도 분리하여 표현하는 것이 좋은가요?도메인의 역할만 명확하다면 API는 통합해서 update 형식으로 만들어도 괜찮은가요?만약, 후자로 처리를 한다면 어디서 처리를 하는게 좋아보이시나요?서비스 계층도메인 계층// MemberModifyService public void update(Long memberId, MemberUpdateRequest request) { Member member = memberFinder.find(memberId); if (request.password() != null) { member.changePassword(request.password()); } if (request.detailRequest() != null) { member.updateInfo(); } } -------- // MemberModifyService public void update(Long memberId, MemberUpdateRequest request) { Member member = memberFinder.find(memberId); member.update(request); } // Member public void update(MemberUpdateRequest request) { if (request.password() != null) { changePassword(request.password()); } if (request.detailRequest() != null) { updateInfo(); } }뭔가, 이런 고민이 계속 드는 이유가 외부 계층에 종속적이지 않고 도메인에 의존하여 개발을 하더라도 실제로 저희가 처한 상황은 대부분 WebAPI 계층에서의 요청이 많다보니 외부의 행위 또한 도메인에 종속되어야 하는가 하는 고민이 생긴 것 같습니다. 양질의 강의 제공해주셔서 감사드립니다!
-
미해결토비의 클린 스프링 - 도메인 모델 패턴과 헥사고날 아키텍처 Part 1
@NaturalIdCache에 대한 보충 설명 및 사용법 공유
'25. 엔티티의 자연키 지정' 영상의 후반부에 적용한 @NaturalIdCache에 대해 추가로 학습한 내용이, 저처럼 해당 애노테이션을 처음 접한 분들에게 도움이 될 것 같아서 글을 작성합니다. 강의에서 오해가 있을 수 있는 부분, 그리고 자연키에 캐시를 적용하는 방법을 정리해 보았습니다. 강의 내용과 실제 동작의 차이점강의에서는 “같은 트랜잭션 안에서 같은 아이디 값을 가지고 여러 번 조회 시 Persistence Context 에 캐시된 값을 꺼내오는 것 처럼. @NaturalIdCache를 적용하면 이것도 영속 컨텍스트에 캐싱이 된다.”고 말씀하셨습니다. 해당 내용에 대한 이해를 돕기 위해 Hibernate의 두 가지 캐시에 대해 간단히 짚고 넘어가겠습니다.1차 캐시 (First-Level Cache): 세션(영속성 컨텍스트) 범위의 캐시입니다. 같은 트랜잭션 안에서만 유효하며, 트랜잭션이 끝나면 사라집니다. Spring Data JPA에서는 기본적으로 @Id 에 대한 조회를 1차 캐시합니다. 2차 캐시 (Second-Level Cache): 세션 팩토리 범위의 캐시로, 여러 세션에서 데이터를 공유할 수 있습니다. 적용하려면 별도의 의존성 추가 및 캐시 관련 설정(@Cache 등)이 필요합니다. 따라서, "같은 트랜잭션 안에서 캐시된 값을 꺼내온다."는 말은 세션 범위의 1차 캐시로 해석됩니다. 하지만 제가 직접 테스트해 본 결과, @NaturalIdCache는 1차 캐시가 아닌 2차 캐시와 관련이 있었으며, 1차 캐시를 적용하기 위해서는 다른 방법이 필요했습니다. 테스트를 통한 확인자연키에 대한 1차 캐시 동작을 확인하기 위해, 강의에서 적용한 Member 엔티티의 @NaturalIdCache 를 제거하고, 자연키(Email)에 @NaturalId만 적용한 상황에서 두 가지 방식으로 테스트를 진행했습니다. 테스트1: findByEmail 메서드를 사용한 조회Java@Test void NaturalIdFirstLevelCache() { Member member = Member.register(createMemberRegisterRequest(), createPasswordEncoder()); memberRepository.save(member); entityManager.flush(); entityManager.clear(); System.out.println("회원 저장 및 persistence context 초기화 완료"); // 같은 email(Natural ID)로 두 번 조회 Member findMember1 = memberRepository.findByEmail(member.getEmail()).get(); Member findMember2 = memberRepository.findByEmail(member.getEmail()).get(); assertThat(findMember1).isSameAs(findMember2); } Spring Data의 쿼리 메서드를 사용하여 이메일로 조회하는 findByEmail 메서드를 만들고, 한 트랜잭션에서 같은 회원을 두 번 조회했습니다. 자연키에 대한 1차 캐시가 동작한다면, SELECT 쿼리는 한 번만 실행되어야 합니다.결과는 SELECT 쿼리가 두 번 실행되었습니다. 즉, 자연키에 대한 1차 캐시가 동작하지 않았습니다. 테스트2: Hibernate의 자연키 관련 API를 사용한 조회@NaturalId를 다루는 글들을 찾아본 결과 Hibernate가 제공하는 자연키 관련 API가 있다는 것을 확인했고, 이를 적용하기 위해 커스텀 리포지토리를 구현했습니다.Java@Repository @RequiredArgsConstructor public class CustomizedMemberRepositoryImpl implements CustomizedMemberRepository { private final EntityManager entityManager; @Override public Optional<Member> findByNaturalId(Email naturalId) { return entityManager.unwrap(Session.class) .bySimpleNaturalId(Member.class) .loadOptional(naturalId); } } 그리고, 테스트 1과 같은 방식으로 테스트를 진행하였습니다. @Test void NaturalIdApi() { Member member = Member.register(createMemberRegisterRequest(), createPasswordEncoder()); memberRepository.save(member); entityManager.flush(); entityManager.clear(); System.out.println("회원 저장 및 persistence context 초기화 완료"); Member findMember1 = memberRepository.findByNaturalId(member.getEmail()).get(); Member findMember2 = memberRepository.findByNaturalId(member.getEmail()).get(); assertThat(findMember1).isSameAs(findMember2); }결과는 SELECT 쿼리가 한 번만 실행되었습니다. 이를 통해 자연키에 대한 1차 캐시는 @NaturalIdCache 애노테이션과 무관하게, 전용 API를 사용해야만 동작하는 것을 확인했습니다. @NaturalIdCache의 용도@NaturalIdCache Javadoc에는 다음과 같은 설명이 있습니다.Specifies that mappings from the natural id values of the annotated entity to the corresponding entity id values should be cached in the shared second-level cache.…중략This annotation is usually used in combination with Cache, since a round trip may only be avoided if the entity itself is also available in the cache.대략 “natural id와 상응하는 id에 대한 매핑을 2차 캐시에 저장하는 애노테이션이고, 엔티티가 캐시되어있어야 하기 때문에 일반적으로 Cache와 함께 사용된다.”라고 해석됩니다. 즉, 1차 캐시가 아닌 2차 캐시를 위한 애노테이션입니다. 정리2차 캐시 관련 설정 및 테스트를 마저 진행한 후 최종 정리한 내용은 다음과 같습니다. 자연키의 1차 캐시@NaturalIdCache 애노테이션과 관련 없습니다. 자연키에 @NaturalId만 붙이면 됩니다.반드시 Hibernate Session의 bySimpleNaturalId() 같은 전용 API를 사용해야 적용됩니다. 자연키의 2차 캐시@Cache와 @NaturalIdCache를 함께 사용해야 동작합니다.@Cache만 사용 시 @Id로 조회할 때만 2차 캐시가 동작합니다.@NaturalIdCache만 사용 시 자연키와 ID에 대한 매핑 정보는 캐시 히트되는 걸 확인했지만, ID와 엔티티에 대한 캐시가 없어서 캐시가 적용되지 않았습니다. @Cache와 @NaturalIdCache 모두 사용 시 ID를 통한 조회와 자연키를 통한 조회 모두 2차 캐시가 적용됩니다. 참고 자료Hibernate6.6 공식 문서NaturalCache javadocsbaeldung: Hibernate Natural IDs in Spring BootSpring Custom Repository 글의 오류나 부족한 내용을 알고 계신 분은 코멘트를 달아주시면 감사하겠습니다.
-
미해결토비의 스프링 6 - 이해와 원리
상태 변경 API 질문
안녕하세요토비님 강의 듣고나서 제 나름대로 API 서버 제작 연습을 좀 해보도가 JPA 및 도메인 상태 변경에 관련해서 질문 드리고 조언을 얻고자 문의 드립니다.상황은 아래와 같습니다.DDD에 입각하여 Aggregator 가 하위 Entity Life Cycle 담당Entity 식별자 타입 경우 LongClient 에서 변경 API 요청 시 Aggregator 의 식별자는 존재하지만, 하위 Entity 에 대한 id 는 포함하지않은채 요청{ id: AggregatorId name: AggregatorName list: [ { name: 'modified name1', type: 'type1', status: 'active' }, { name: 'modified name2', type: 'type2', status: 'inactive' }, { name: 'new name3', type: 'type3', status: 'active' } ] } id 가 없이 JPA 변경 요청 시 delete-insert 가 진행 될것 같아 자체적으로 key based diff 라는 함수를 제작하여 변경점과 신규 데이터에 대해서 병합 후 save() 를 호출 할 것같은데 이럴 경우 insert, update 문의 별도로 나가지 않을까 생각이 듭니다.이런 경우 DB 입장에서 하나의 트랜잭션에서 수행하더라도 부하나 성능에 악영향을 미칠 것 같은데 이럴 경우 어떤식으로 애플리케이션 로직을 세우고 JPA 를 어떻게 활용해야되는지 조언 부탁드려도될까요?참고로 저는 하위 Entity 변경 시 batchUpsert 를 고려하고 있습니다.
-
미해결토비의 클린 스프링 - 도메인 모델 패턴과 헥사고날 아키텍처 Part 1
jpa/도메인 엔티티 분리에 대한 궁금한 점이 있습니다.
토비님 안녕하세요. 강의 너무 즐겁게 잘 수강하고 있는 한 개발자 입니다.강의 수강 중에 궁금한 점이 생겨서, 토비님의 의견이 궁금해서 한 가지 질문 드리고자 합니다.33. 엔티티 클래스와 JPA 매핑 정보 분리강의에서 분리를 xml로 분리해서 매핑 하는 예를 들어주셔서, 이 부분에 대해서 궁금한 점이 있습니다. 개인적으로 xml 매핑작성 생산 비용과 jpa/도메인 엔티티를 분리해서 작성하는 비용이 크게 차이나지 않는것 같단 생각이 들긴합니다. 결국 xml이든, 코드든 분리해서 작성 비용이 필요한 것 같아요.그렇다면, 여기에서 관리 포인트를 이중(xml,코드)으로 가져가는게 나을지, jpa/도메인 엔티티를 분리해서 코드에서 관리하는게 나을지? 고민이 되는데요. 토비님의 의견은 어떠신지 궁금합니다.ai 자동완성 기능 활용코드도 마찬가지로, 애노테이션 빼줘, 붙여줘 하면 어느정도 잘 만들어주긴 하더라구요. 이 부분도 어떻게 생각하시는지 궁금합니다.코파일럿에 xml 매핑정보 만들어줘 하는 내용과, 코드로 애노테이션 붙여줘, 빼줘 해서 복/붙하는 행위 자체가 크게 다르지 않은것 같다는 생각이 들긴해서 이 부분은 어떻게 생각하시는지도 궁금합니다.유지보수 관점에서생산비용 보다, 개발 완료 후 유지보수를 하는데 있어서 그래도 xml/코드 두가지 중 1개를 선택해야한다면 어떻게 관리하는것이 나을까요?? 바쁘신 와중에도 질문 확인하고 답변 주시는 점 미리 감사드립니다.
-
미해결토비의 클린 스프링 - 도메인 모델 패턴과 헥사고날 아키텍처 Part 1
안녕하세요! 강의 완강했습니다! 혹시 다음 강의는 대략적으로 언제 오픈 될까요?
안녕하세요! 강의 완강했습니다! 혹시 다음 강의는 대략적으로 언제쯤 오픈 될까요?