묻고 답해요
158만명의 커뮤니티!! 함께 토론해봐요.
인프런 TOP Writers
-
미해결토비의 클린 스프링 - 도메인 모델 패턴과 헥사고날 아키텍처 Part 1
DTO를 서비스 레이어에서 사용할 수 밖에 없다면
엔티티를 쓰지 못하는 상황JDBC Template나 nativeQuery처럼 직접 조회가 필요한 경우에는 엔티티를 사용하기 힘들 것 같은데이런 경우에 DTO를 사용하게 되면 서비스 레이어에 해당 부분이 생길거 같은데 이 정도는 괜찮은 걸까요?
-
미해결토비의 클린 스프링 - 도메인 모델 패턴과 헥사고날 아키텍처 Part 1
강의 업데이트 내역 질문
안녕하세요.강의 업데이트가 된 거 같은데 혹시 업데이트 된 내역을 확인 할 수 있는 곳이 있을까요? 어떤 부분이 바뀌었는지 궁금합니다~
-
미해결토비의 클린 스프링 - 도메인 모델 패턴과 헥사고날 아키텍처 Part 1
"MemberFinderTest, MemberRegisterTest" record관련
record + 클래스 레벨 @Transactional에서 에러가 발생합니다.!질문은 record 예약어는 final 클래스라서 상속이나 프록시 생성이 불가능한거 같은데@Transactional 어노테이션은 Spring AOP기반이라 프록시 객체 생성이 안되서 에러가 발생하는거 같은데... 강의에서는 통과가 되더라구요 제가 어떤 부분이 놓쳤을까요?
-
해결됨토비의 클린 스프링 - 도메인 모델 패턴과 헥사고날 아키텍처 Part 1
인터페이스
안녕하세요! 이번 강의 수강 중 서비스 인터페이스에 대해 궁금하여 글을 남기게 되었습니다. 흔히 스프링 프로젝트에서 보통 Service 인터페이스와 ServiceImpl 구현체로 나누어 개발하며 제가 참여하게 된 프로젝트 또한 이러한 구조였습니다.이미 이전 개발자 분께서 Service 인터페이스와 ServiceImpl 구현체로 나눠놓은 상태였고, 제가 A 기능의 AServiceImpl 안에 특정 서비스 로직이 비대해짐에 따라 해당 코드가 명확하지 않다고 생각되어 메서드로 분리하게 되었습니다. A 엔티티의 상태, 계산 등의 로직으로 private 메서드를 두었습니다.여기서 동료와 의견이 갈렸습니다:동료 입장:1. ServiceImpl 안의 모든 메서드는 반드시 인터페이스에 있어야 한다.2. 인터페이스는 외부·내부 구분이 아니라, 구현체가 제공하는 기능을 보장하는 것이므로 전부 담아야 한다.3. 내부 메서드를 둘 거라면 굳이 인터페이스로 추상화할 필요가 없고 그냥 클래스로 쓰면 된다.제 입장:1. 인터페이스에는 “외부 계약(=공개 API)”만 있어야 한다.2. 구현체 내부에서만 쓰이는 로직은 private으로 감추는 게 맞고, 외부에서 호출할 필요가 전혀 없다.3. 인터페이스를 구현체 내부 헬퍼까지 다 포함하면, 오히려 계약이 불필요하게 비대해지고 역할이 모호해진다.핵심 쟁점저는 public 메서드는 모두 인터페이스에 있어야 한다는 데에는 동의합니다. 또한, 단순 public, private 만 있다면, 인터페이스의 역할이 모호하다는 것또한 이해하지만, 이전 코드와의 일관성과 운영 중인 시스템에 대한 변경이 필요할정도의 중대사항이라고 생각하지 않습니다.궁금한 점은 private/내부 헬퍼 메서드까지 인터페이스에 강제로 올리는 게 맞는지, 인터페이스를 사용하는 의의가 궁금합니다. 긴글 읽어주셔서 감사합니다.
-
해결됨토비의 클린 스프링 - 도메인 모델 패턴과 헥사고날 아키텍처 Part 1
배포 시 테스트 코드가 돌아갈때 사용하게 될 RDB 셋팅에 관하여..
안녕하세요.강의 정말 잘 들었습니다.도움이 많이 되어서 수강평은 나중에 꼼꼼히 작성해볼 생각입니다~!====================== 바쁘시면 이 부분만 읽으셔도 됩니다. ============= 배포 시 jenkins server에서 실제 서버에 배포할때 테스트가 돌아갈텐데jenkins server에 compose를 통해 작동 하게 될mysql에 DB schema insert 작업을 해서동일하게 구조를 맞추고 테스트가 돌아가게 하는 게 좋은 생각일 까요?=========================================================== [세부 사항] 일단 테스트 코드가 local mysql에서 돌아가는 걸로 이해 했습니다. 배포 시 jenkins server에서 실제 서버에 배포할때 테스트가 돌아갈텐데jenkins server에 compose를 통해 작동 하게 될mysql에 DB schema insert 작업을 해서동일하게 구조를 맞추고 테스트가 돌아가게 하는 게 좋은 생각일 까요?현재 아직 jenkins 배포를 하고 있지는 않고 소스코드 개발중이라머리속으로만 생각하는 상태입니다. 토비님 의견은 어떠신가요.....?
-
해결됨토비의 클린 스프링 - 도메인 모델 패턴과 헥사고날 아키텍처 Part 1
Exception 정의 기준
application 쪽에서는 커스텀 예외를 정의해서 사용했는데 Profile과 Email 레코드에서 값 형식 검증 부분에서는 표준 예외를 사용하셨더라구요 예외를 분리해서 사용하는 기준이 무엇일까요?
-
미해결토비의 클린 스프링 - 도메인 모델 패턴과 헥사고날 아키텍처 Part 1
개인적질문
안녕하세요 토비님 가의 잘 보고 있습니다. DDD는 처음이고 헥사고날방식도 처음이라 질문 수준이 낮은 점 양해 부탁드립니다!이 강의까지 보고나서 전체코드를 봤을떄 궁금한 점들이 있어서 질문 드립니다!1번 질문:@Entitypublic class Member { @Embedded private Address homeAddress;} @Embeddable public class Address { private String city; private String street; private String zipcode;}@Embeded사용할때 데이터 필드를 의미 있는 하나의 묶음 이라는 의미로 사용하는데 profile 이나 email같은 경우에는 컬럼이 하나여도 @Embeded 로 묶으셨는데 그 기준(언제 사용해야하느지)가 궁금합니다. 저라면 그냥 생각 없이 String 으로 받았을거같은데 ….!2번 질문 : Aggregate root 로 접근해하면 메서드 체이닝 비슷하게 일어나는거 같은데 그런 점은 괜찮나요!?3번 질문 :. Aggreagate root로 접근하는 방식은 DDD에서만 사용하는지 ?? 기존 JPA + 레이어 아키텍쳐에서도 사용이 가능한건지....?4번 질문 : 강의에서는 1:1 이였지만 , ManyToOne 단방향 , ManyToOne OneToMany 양방향 관계에서도똑같이 Aggregate root로 접근해서 데이터를 처리해야하는지..?미리 감사드립니다!!
-
해결됨토비의 클린 스프링 - 도메인 모델 패턴과 헥사고날 아키텍처 Part 1
어플리케이션 , 도메인 계층질문
안녕하세요 토비님어플리케이션 계층의 port는 도메인과 외부의 연결을 위해 필요 하다고 알고 있는데jpa나 마이바티스 같은 기술 또한 도메인 외부의 내용이기에 인프라 계층으로 들어간다고 알고 있습니다그런데 의존성 역전을 위한 레파지토리 인터페이스를 어플리케이션 계층에 만들면 어플리케이션 서비스에서 도메인에 관련된 내용을 너무 깊게 관여하는 것 같아서 질문 드립니다어플리케이션과 도메인의 경계를 어디까지로 보는게 좋을까요
-
미해결토비의 클린 스프링 - 도메인 모델 패턴과 헥사고날 아키텍처 Part 1
record class @Transactional 사용에 대해서
안녕하세요 토비님 회원 애플리케이션 서비스 테스트 (2) -25:19에 대해 질문 드립니다. record 에 @Transactional을 사용하면 실행에는 문제가 없으나 인텔리제이에서 Classes annotated with '@Transactional' could be implicitly subclassed and must not be final 이와같은 경고 메세지를 주는데 무시하고 지나가도 되는건지요?
-
해결됨토비의 클린 스프링 - 도메인 모델 패턴과 헥사고날 아키텍처 Part 1
39. 문서와 코드 다듬기 updateInfo 테스트 질문 있습니다.
토비님 안녕하세요. 39. 문서와 코드 다듬기 강의의 updateInfo 테스트 부분 질문이 있습니다. 강의 28:30 부근에서 updateInfoFail()에 코멘트를 달게 되는데 성공케이스에 대한 코드는 updateInfo() 부분으로 옮기는 것도 좋아보이는데 토비님 생각이 궁금합니다. updateInfoFail()의 마지막에 member가 member2의 프로필 주소와 중복되는 테스트가 있으므로, member2의 프로필 주소를 설정해주는 코드가 필요하지만, 하나만 있어도 괜찮지 않을까 생각합니다.그래서 나머지 성공에 대한 코드는 updateInfo() 메소드로 옮겨 "상세 정보 수정이 가능한 경우"들을 나열하는게 좋다고 생각했습니다. 똑같은 준비작업이 반복되다보니 한 번 세팅해놓고 여러 케이스를 다 검증해보는 것이 낫다고 하셨는데, 현재 테스트 클래스 메소드가 성공/실패로 나뉘어져있어 성공하는 부분은 다른 메소드로 이동하는게 좋아보인다 생각했습니다. 아래 코드에서 성공 케이스와 실패 케이스로 나누어보았습니다.코드updateInfo()@Test void updateInfo() { Member member = registerMember(); Long memberId = member.getId(); memberRegister.activate(memberId); entityManager.flush(); entityManager.clear(); var updateRequest = createMemberInfoUpdateRequest(); member = memberRegister.updateInfo(memberId, updateRequest); assertThat(member.getDetail().getProfile().address()).isEqualTo(updateRequest.profileAddress()); // 기존 프로필 주소로 계속 변경 요청 가능 memberRegister.updateInfo(memberId, updateRequest); // 다른 프로필 주소로 변경 가능 memberRegister.updateInfo(memberId, createMemberInfoUpdateRequest("omg123")); // 프로필 주소 제거 가능 memberRegister.updateInfo(memberId, createMemberInfoUpdateRequest("")); } updateInfoFail()@Test void updateInfoFail() { Member member = registerMember(); Long memberId = member.getId(); memberRegister.activate(memberId); member = memberRegister.updateInfo(memberId, createMemberInfoUpdateRequest()); Member anotherMember = registerMember("another@email.com"); Long anotherMemberId = anotherMember.getId(); memberRegister.activate(anotherMemberId); entityManager.flush(); entityManager.clear(); // anotherMember가 member와 프로필 주소 중복 MemberInfoUpdateRequest duplicateProfileUpdateRequest = createMemberInfoUpdateRequest(member.getDetail().getProfile().address()); assertThatThrownBy(() -> { memberRegister.updateInfo(anotherMemberId, duplicateProfileUpdateRequest); }).isInstanceOf(DuplicateProfileException.class); // member와 중복되지 않는 프로필 주소로는 변경 가능 MemberInfoUpdateRequest updateRequest = createMemberInfoUpdateRequest("profile123"); memberRegister.updateInfo(anotherMemberId, updateRequest); // member가 anotherMember와 프로필 주소 중복 assertThatThrownBy(() -> { memberRegister.updateInfo(memberId, updateRequest); }).isInstanceOf(DuplicateProfileException.class); } 좋은 강의 해주셔서 감사드립니다!
-
미해결토비의 클린 스프링 - 도메인 모델 패턴과 헥사고날 아키텍처 Part 1
섹션 6 -2강
xml관련 설정강의자료로 남겨주신다고 했는데 어디서 찾을 수 있나요?
-
미해결토비의 클린 스프링 - 도메인 모델 패턴과 헥사고날 아키텍처 Part 1
unique-constraint 설정 질문드립니다.
orm.xml unique-constraint 설정 부분 설명해주신 부분에서 인덱스로서 성능을 위해서, 데이터 중복저장 문제를 위해서 설정을하면 좋다고 말씀해주셨는데요<index unique="true">설정의 차이점이 뭔지 잘모르겠습니다
-
미해결토비의 클린 스프링 - 도메인 모델 패턴과 헥사고날 아키텍처 Part 1
@ResponseBody 로 도메인 레이어의 MemberRegisterRequest 를 그대로 사용하는 것에 대해서
안녕하세요. 토비님, 강의 잘 듣고 있습니다. 🙂오늘은 강의 내용에서 좀 굉장히 의외인 부분을 발견해서 질문드립니다.강의 #41. MemberApi와 웹 단위 테스트 에서 MemberRegisterRequest 가 domain 레이어에서 정의했던 클래스임에도, @RequestBody 파라미터 그대로 쓰셨는데, 이 부분이 많이 의외고 우려가 되었습니다.저렇게 하면 MemberRegisterRequest 클래스의 코드 변경이 api 스펙 변경을 의미하는지가 코드리뷰 상에서 쉽게 보이지 않고 숨겨질 수 있다는 염려가 됩니다.실제로 MemberRegisterRequest 에 필드를 추가해서 PR 을 올리면 코드리뷰어가 봤을 때 domain 레이어의 특정 모델에 필드가 추가됐을 뿐인 작은 변경으로 보일 것입니다. 그래서 그것이 어느어느 API 의 스펙에 영향을 주는지 알기가 너무 어려울 것 같습니다.그래서 저는 API 의 스펙이 되는 Request, Response DTO 의 경우 반드시 클래스를 별도로 분리해야한다고 생각합니다.API 스펙은 server 마음대로 변경할 수 있는 서버만의 코드가 아니라 client 와의 계약 문서라고 보기 때문입니다.그래서 Request/Response 같이 백앤드 엔지니어가 함부로 변경할 수 없는 영역과 맘대로 변경 가능한 영역을 분리해서, 어플리케이션과 도메인 로직의 변화가 API 스펙 변경으로 인한 장애 걱정으로 이어지지 않게 하는 것이 중요하다고 생각합니다.이게 근데 단순히 클래스 분리만 해둬도 PR 에서 API 스펙이 어떻게 바뀌는지 쉽게 트래킹이 가능해지기 떄문에 이 부분 만큼은 번거롭더라도 실보다 득이 훨씬 많아서 꼭 분리해야한다고 생각해왔습니다.이 부분에 대해서 어떻게 생각하시는지 궁금합니다.
-
미해결토비의 클린 스프링 - 도메인 모델 패턴과 헥사고날 아키텍처 Part 1
안녕하세요 토비님!
안녕하세요 토비님! 강의를 보다가 궁금한 점이 생겨서 질문 드립니다. 테스트 코드 작성시 EmailSender같은 경우나 , 외부 요인(?) 같은 경우에 저는 테스트 코드가 외부요인에 의해 영향받기를 원하지 않아 @MockitoBean을 사용하는데요 그런데 강의에서는 왜 @MocktioBean을 사용하시지 않고 @TestConfiguration을 사용하셨는지 궁금합니다!감사합니다!
-
해결됨토비의 클린 스프링 - 도메인 모델 패턴과 헥사고날 아키텍처 Part 1
자신의 정보만 업데이트 하는 로직 궁금한 점
38강 10분 13초에서 자신의 정보를 업데이트 하는 로직 만드는 부분에서 궁금한 점이 생겼습니다.로그인된 사용자만 자기 정보를 업데이트 할 수 있는 기능을 웹 API 쪽의 어댑터에서 만든다고 하셨는데 왜 그런걸까요? 애플리케이션에서 검증을 하면 안되는걸까요?
-
미해결토비의 클린 스프링 - 도메인 모델 패턴과 헥사고날 아키텍처 Part 1
디스코드 채널 입장이 안돼요!!
어떻게 들어가야되죠?!
-
미해결토비의 클린 스프링 - 도메인 모델 패턴과 헥사고날 아키텍처 Part 1
Member를 activate 시 deactiavatedAt 초기화 필요성 질문입니다
Member activate 호출 시 MemberDetail의 activate 또한 호출 하게 되는데요.이때 MemberDetail의 activate 로직에서 this.deactivatedAt = null 을 통해 비활성 일시는 초기화 해주어야활성 -> 비활성 -> 활성 -> 비활성 시 deactivatedAt이 null임으로 문제가 없을 것 같은데 어떻게 생각하시나요?
-
해결됨토비의 클린 스프링 - 도메인 모델 패턴과 헥사고날 아키텍처 Part 1
MemberRegisterTest에서 @SpringBootTest 질문
MemberRegisterTest를 진행할 때 @SpringBootTest를 사용해서 테스트를 진행했는데요서비스 테스트에는 @ExtendWith(MockitoExtension.class)를 사용하는 경우를 많이 봤습니다 헥사고날 아키텍처에서는 애플리케이션과 도메인이 중심이 되기 때문에 서비스에서 @SpringBootTest를 사용한걸까요?
-
미해결토비의 클린 스프링 - 도메인 모델 패턴과 헥사고날 아키텍처 Part 1
ApiControllerAdvice에서 2개 이상의 Exception 타입 핸들링
안녕하세요.ApiControllerAdvice에서 하나의 Handler 메서드에서 아래 2개 Exeception 타입을 처리하시도록 변경하셨는데요.- DuplicateEmailException- DuplicateProfileException 이렇게 할 경우, 아래와 같이 두 Exception의 공통 타입인 RuntimeException 객체로 파라미터를 받아야 하는거 아닐까요?@ExceptionHandler({DuplicateEmailException.class, DuplicateProfileException.class}) public ProblemDetail duplicateExceptionHandler(RuntimeException e) { return getProblemDetail(HttpStatus.CONFLICT, e); } 감사합니다.
-
미해결토비의 클린 스프링 - 도메인 모델 패턴과 헥사고날 아키텍처 Part 1
Member와 MemberDetail의 연관관계 주인이 바뀐게 아닌가 싶습니다.
안녕하세요.Member와 MemberDetail의 연관관계 주인이 바뀐게 아닌가 싶습니다.비록 1:1 관계이고, 두 객체 인스턴스가 동시에 생성되고 테이블에 영속화 되게끔 설정된 거는 맞지만,논리적으로 Member 엔터티가 상위 엔터티이고, MemberDetail이 하위 엔터티가 맞는 것 같아요.추후 Member 엔터티를 참조하는 다른 엔터티가 만들어질텐데,MemberDetail을 참조하는게 아니라 Member를 참조해야 하고요.관련한 의견 부탁 드립니다.감사합니다.