묻고 답해요
158만명의 커뮤니티!! 함께 토론해봐요.
인프런 TOP Writers
-
미해결토비의 클린 스프링 - 도메인 모델 패턴과 헥사고날 아키텍처 Part 1
도메인 모델에서의 검증과 애플리케이션 레벨 검증의 경계
도메인 모델에서의 검증과 애플리케이션 레벨 검증의 경계에 대해 질문드립니다.현재 도메인 모델에서는 이메일 형식이 올바른지, 닉네임이나 비밀번호가 null이 아닌지 같은 최소한의 조건만 검증하고 있습니다. 반면, 비밀번호가 8자 이상인지, 닉네임이 5자 이상인지 같은 검증은 애플리케이션 레이어에서 처리하고 있습니다. 그런데 닉네임이 5자 이상이어야 한다 같은 규칙도 도메인 규칙으로 볼 수 있지 않을까 라고 생각이 들어 해당 검증 역시 도메인 모델에서 처리하는게 맞지 않나 라는 생각이 드는데,도메인 모델에서의 검증과 애플리케이션 레벨 검증의 경계는 어디까지 두는 게 좋은지 토비님 의견이 궁금합니다.말씀하신것을 토대로 예측해 봤을때 형식이나 정책적 요구사항(변경 가능성이 있는 규칙)은 애플리케이션 레이어에서, 도메인의 본질적 불변 조건은 도메인에서 검증하는 걸까요?
-
미해결실전! Querydsl
querydsl 설정 문제
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.1. 강의 내용과 관련된 질문을 남겨주세요.2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요.(자주 하는 질문 링크: https://bit.ly/3fX6ygx)3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요.(질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG)질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요.=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예/아니오)예2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오)예3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오)예[질문 내용]여기에 질문 내용을 남겨주세요. autoplugins { id 'java' id 'org.springframework.boot' version '3.2.0' id 'io.spring.dependency-management' version '1.1.4' } group = 'study' version = '0.0.1-SNAPSHOT' sourceCompatibility = '17' configurations { compileOnly { extendsFrom annotationProcessor } } repositories { mavenCentral() } dependencies { implementation 'org.springframework.boot:spring-boot-starter-data-jpa' implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'com.github.gavlyukovskiy:p6spy-spring-boot-starter:1.9.0' compileOnly 'org.projectlombok:lombok' runtimeOnly 'com.h2database:h2' annotationProcessor 'org.projectlombok:lombok' testImplementation 'org.springframework.boot:spring-boot-starter-test' //test 롬복 사용 testCompileOnly 'org.projectlombok:lombok' testAnnotationProcessor 'org.projectlombok:lombok' //Querydsl 추가 implementation 'com.querydsl:querydsl-jpa:5.0.0:jakarta' annotationProcessor "com.querydsl:querydsl-apt:${dependencyManagement.importedProperties['querydsl.version']}:jakarta" annotationProcessor "jakarta.annotation:jakarta.annotation-api" annotationProcessor "jakarta.persistence:jakarta.persistence-api" } tasks.named('test') { useJUnitPlatform() } clean { delete file('src/main/generated') } 교안에 나온 대로 설정해 준 상태인데 현재 q타입 클래스는 생성 되었지만 intellij가 이를 정상적인 클래스로 인식하지 못해서 QHello를 import하지 못하고 있는 상황입니다
-
미해결토비의 클린 스프링 - 도메인 모델 패턴과 헥사고날 아키텍처 Part 1
Member#register() 메서드명이 모호하게 느껴집니다.
Member#create() 메서드를 register로 공통언어를 바꾸셨는데, 뜻이 모호해진 것 같아서 질문드립니다! 제가 생각하기에 register(등록)이라는 단어는 생성과 영속화라는 두가지의 행위를 함축한 단어로 느껴집니다. 따라서 MemberService#register()는 너무 자연스럽습니다. 실제로 Member를 생성한 후에 MemberRepository를 통하여 영속화까지 하는 내용으로 구현되어있습니다.하지만 Member#register()는 이와 다르게 Java 객체를 생성하기만 하는 것이라, 메서드명과 실제 동작이 불일치한다고 느껴집니다. 하지만 또 동시에 도메인 모델을 글로 작성하는 과정에서 '멤버를 등록한다.' 라는 말을 쓰는 것은 자연스럽습니다. 그 구현이 코드적으로 Member에 있는 것만이 부자연스럽습니다.이런 경우에는 Member라는 객체만으로 도메인 모델을 온전히 표현해내기가 어려운 것일까요? 해당 모델을 표현하기 위해서는 MemberService 같은 코드가 꼭 필요한 걸까요?
-
미해결토비의 클린 스프링 - 도메인 모델 패턴과 헥사고날 아키텍처 Part 1
EntityManager#flush()를 검증하면 더 좋을 것 같습니다!
멤버를 저장하는 테스트에서 EntityManager#flush()를 호출해서 플러시되어 DB까지 쿼리문이 제대로 가는지 검증하시는 것을 보았습니다. (저는 플러시까지 테스트해볼 생각을 못했어서 아주 인상깊었습니다 ㅎㅎ) 하지만 해당 부분이 검증문 없이 홀로 호출되고 있습니다.좀더 의도를 드러내기 위해서는 assertThatCode(() -> em.flush()).doesNotThrowAnyException() 처럼 코드를 작성하는게 더 좋지 않을까 싶어서 질문드립니다!적용 예시@Test void 멤버를_저장한다() { var member = MemberFixture.createAny(); memberRepository.save(member); assertThat(member.id()).isNotNull(); assertThatCode(() -> em.flush()) .doesNotThrowAnyException(); }
-
미해결토비의 클린 스프링 - 도메인 모델 패턴과 헥사고날 아키텍처 Part 1
테스트클래스명이 테스트 목적을 잘 나타내지 못하는 것 같습니다.
MemberRepositoryTest는 EntityManager 나 @DataJpaTest 등 Jpa와 깊게 관련되어있는 테스트로 이해됐습니다. EntityManager#flush() 가 잘 호출되는지 확인하는 부분도 그런 이유에서 이해됩니다.하지만 테스트명이 MemberRepositoryTest인 점에서 헷갈리는 부분이 발생합니다. MemberRepository 인터페이스의 메서드를 테스트하는 것이 목적이라면 좀 더 추상화된 테스트를 작성하는 것이 옳지 않았나? 라는 것이 주된 궁금증입니다. 물론 Spring Data JPA 특성상 런타임에 구현체가 만들어지기 때문에, 테스트 대상은 인터페이스를 사용해야 한다는 것은 이해합니다만, 최소한 클래스명은 좀 더 구체적으로 작성해주는 것이 의도에 부합하지 않나? 해서 질문드립니다! 어떻게 생각하시나요?
-
미해결토비의 클린 스프링 - 도메인 모델 패턴과 헥사고날 아키텍처 Part 1
도메인 로직으로 분리해도 되나요?
해당 코드에서 자기 자신의 프로필 address와 비교하는 로직을 도메인에게 맡기는 건 어떨까요? 토비님! 이렇게 해도 되는건지 아니면 따로 빼신 이유가 있으신 지 궁금합니다. private void checkDuplicateProfile(Member member, String profileAddress) { if (profileAddress.isEmpty()) return; Profile currentProfile = member.getDetail().getProfile(); if (currentProfile != null && currentProfile.address().equals(profileAddress)) return; if (memberRepository.findByProfile(new Profile(profileAddress)).isPresent()) { throw new DuplicateProfileException("이미 존재하는 프로필 주소입니다: " + profileAddress); } }이런 식으로요!private void checkDuplicateProfile(Member member, String profileAddress) { if (profileAddress.isEmpty()) return; if (!member.isProfileNull() && member.isProfileEquals(profileAddress)) return; if (memberRepository.findByProfile(new Profile(profileAddress)).isPresent()) { throw new DuplicateProfileException("이미 존재하는 프로필 주소입니다: " + profileAddress); } }
-
미해결토비의 클린 스프링 - 도메인 모델 패턴과 헥사고날 아키텍처 Part 1
도메인에 대한 개인적인 경험이 다음 설계에 영향을 주는 경우가 많습니다. 토비님께서는 설계를 하실 때, 이전 경험에서 비롯된 도메인을 어느 정도까지 설계에 반영하시나요?
안녕하세요, 토비님. 주니어로서 아직 부족한 점이 많지만, 궁금한 점이 있어 또 이렇게 질문드립니다.Section 3에서 소개된 splearn 도메인 강의를 듣기 전에, 스스로 먼저 도메인 모델링을 해보는 시간을 가졌습니다.그 과정에서 회원, 수강, 강의 등의 개념 외에 수강신청이라는 도메인을 도출했는데요, 그 이유는 “수강은 수강신청 후 결제가 완료되어야 성립된다”는 제 안의 암묵적인 경험에 의한 도메인 해석 때문이었습니다.그런데 사실 강의에서 제시된 사용자 스토리에는 결제라는 개념이 존재하지 않았습니다. 그럼에도 불구하고, 과거 경험에서 비롯된 도메인이 자연스럽게 설계에 스며들었고, 결과적으로 오버엔지니어링으로 이어졌다는 생각이 들었습니다.이런 식의 ‘의도되지 않았지만 그럴듯한 도메인’을 설계에 넣고 싶은 유혹은 실제 회사에서 설계할 때도 자주 느끼는 부분입니다.그래서 토비님께서는 이런 상황에서 어떤 기준으로 도메인을 포함하거나 배제하시는지, 그리고 본인의 경험을 어떤 방식으로 설계에 녹이시는지가 궁금해졌습니다.바쁘시겠지만, 짧은 의견이라도 들려주시면 큰 도움이 될 것 같습니다. 감사합니다!
-
미해결토비의 클린 스프링 - 도메인 모델 패턴과 헥사고날 아키텍처 Part 1
인터페이스 위치를 결정하는 기준에 관해
안녕하세요 토비님, 강의 너무 재미있게 듣고 있습니다.인터페이스를 도메인 계층에 둘지, required 포트에 둘지 결정하는 기준이 있을까요?Splearn의 코드를 예로 들어보면, 도메인 서비스 인터페이스인 passwordEncoder는 어댑터 계층에서 구체적인 기술로 구현된다는 점에서 EmailSender, MemberRepository 같은 required 포트의 인터페이스와 유사하게 느껴집니다. 이들 모두 외부 세계(또는 특정 기술)와 애플리케이션을 분리하는 역할을 하기 때문입니다.만약 애플리케이션 서비스에서 passwordEncoder를 사용해 비밀번호를 암호화한 후, 암호화된 비밀번호(passwordHash)를 Member.register 메서드에 인자로 직접 넘겨준다면, passwordEncoder 역시 required 포트에 위치할 수 있지 않을까요? 도메인 계층도 애플리케이션 서비스에 의존하지 않게 되고요.그런데도 불구하고 passwordEncoder를 도메인 계층의 인터페이스로 두신 이유가 궁금합니다.감사합니다.
-
미해결토비의 클린 스프링 - 도메인 모델 패턴과 헥사고날 아키텍처 Part 1
record 생성 시 휴먼 에러 발생 가능성
안녕하세요, 토비님. 메서드 파라미터가 많아질 경우 record 타입의 parameter object를 사용하여 가독성을 높일 수 있는 방법을 소개해주셨는데요,이 방법으로도 parameter object 인스턴스 생성 시 argument를 잘못 전달한다거나 등의 휴먼 에러는 여전히 발생 가능하지 않나 생각이 듭니다.java record도 kotlin의 named parameter와 같은 기능을 제공하면 좋겠지만 검색해봤을땐 아직 제공하지 않는 것 같더라구요. 이처럼 argument 전달 버그는 예방할 방법이 없을까요? 혹은 강의 뒷부분에서 이러한 점을 개선해주실까요?record에 builder를 사용해야 하나 생각이 들긴 하는데, 괜찮은 방법인지는 물음표네요.
-
미해결토비의 클린 스프링 - 도메인 모델 패턴과 헥사고날 아키텍처 Part 1
토비님이 생각하시는 복잡한 도메인은 무엇이라고 생각하시나요?
안녕하세요. 토비님강의가 마치 의도하신 것 처럼 시니어 개발자님과 함께 개발하면서 의논하는 느낌으로 진행되어서 재밌게 잘 수강하고 있습니다.강의 내용에서 DDD는 도메인이 복잡해진 순간에 택해야 한다. 라는 말씀을 해주셨는데요.문득, "토비님이 생각하시는 도메인이 복잡해진 순간은 어떤 순간일까? 나름의 기준이 있으신걸까?" 하는 생각이 들었습니다.개인적으로 저는 어떻게 생각할까 라고 고민을 해봤는데요. 1) 도메인을 설명하기 위해서 개발자가 코드를 살펴봐야 하는 순간2) 각 도메인 간의 결합도가 고객의 유스케이스를 해결하기 위해 다양하게 결합되어야 하는 경우이 두 가지가 떠올랐는데, 토비님의 견해가 궁금해서 질문드려봅니다!늘 양질의 강의를 제공해주셔서 감사드립니다. (_ _)
-
미해결실전! Querydsl
quey dsl 설정부분
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.1. 강의 내용과 관련된 질문을 남겨주세요.2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요.(자주 하는 질문 링크: https://bit.ly/3fX6ygx)3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요.(질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG)질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요.=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예[질문 내용]강의 내용대로 설정했는데 qtype이 만들어지고 있지 않습니다 스프링부트 버전이 올라가고 나서 qtype설정이 바뀐거 같은데요 추가 내용을 올려주셨으면 좋겠어요 ㅠㅠㅠ
-
미해결토비의 클린 스프링 - 도메인 모델 패턴과 헥사고날 아키텍처 Part 1
Email Pattern 테스트 검증 관련 질문이 있습니다.
안녕하세요 토비님, 강의 너무 잘 듣고 있습니다.이메일 패턴 테스트 관련해서 궁금한 부분이 생겨 질문을 드립니다.해당 강의를 기준으로 기존 : Member 엔티티에서 패턴 검증변경 후 : Email 값객체에서 패턴 검증 변경 후 단계에서 Email 값 객체에 패턴 검증하는 로직이 들어갔기에 해당 로직에 대한 테스트는 EmailTest 에 들어가야 하지 않는가? 라는 생각이 들어요.해당 부분에 대해 의견을 여쭙고 싶습니다.감사합니다.
-
해결됨토비의 클린 스프링 - 도메인 모델 패턴과 헥사고날 아키텍처 Part 1
Mybatis같은 sql mapper를 사용시..
안녕하세요 토비님! 강의 너무 잘듣고 있습니다. 애그리거트 쪽 강의를 들으면서 문득 든 생각을 질문드립니다.현재 사내에서 하고 있는 프로젝트는 JPA를 전혀사용하지 않고 Mybatis만을 사용하고 있는데요 이런 경우에도 에그리거트라던가 헥사고날 아키텍쳐를 적용하면서 진행하는게 충분히 가능할까요? 그게 아니라면 기존에 적용하고 있는 흔히 말하는 레이어드 아키텍쳐 및 트랜잭션 스크립트 방식으로 진행하는게 더 나은 방법일까요?혹시라도 수준 떨어지는 질문이라면 사과드립니다.감사합니다.
-
해결됨토비의 클린 스프링 - 도메인 모델 패턴과 헥사고날 아키텍처 Part 1
mise도 사용하시나요?
안녕하세요, 토비님.5강 개발 환경 구성(2)에서 sdk man을 소개해주셨는데요,저는 처음 보는 서비스라 찾아보니 최근에는 편의성 등을 이유로 mise를 많이들 사용하는 것 같더라구요. 강의에서는 mise 대신 sdk man을 사용하신 이유가 궁금합니다, 아무래도 java만 다루는 강의다보니 sdk man을 선택하신 걸까요?토비님께서도 실무나 개인적으로는 mise를 사용하시는지 혹은 다른 서비스를 사용하시는지도 궁금합니다.
-
미해결토비의 클린 스프링 - 도메인 모델 패턴과 헥사고날 아키텍처 Part 1
`Objects.requireNonNull()`을 계속 사용하시는 이유가 궁금합니다.
15강에서 스프링의 @NonNull 지원과 SpotBugs를 통해서 null 안정성을 확보하신 내용을 봤습니다. 그런데 계속해서 Objects.requireNonNull() 을 사용하시는 것을 보고 의문이 들었습니다. 이미 Member 객체의 null 안정성은 확보된 것이 아닌가요?굳이 Objects.requireNonNull() 을 계속 남기시는 이유를 생각해봤는데, 애노테이션+도구의 조합과 함께 코드적으로 null 안정성을 확보하고 싶으신 것으로 이해했습니다. (해당 도구가 사라져도 코드 수준의 안정성은 남아있기 때문에, 코드를 수정할 필요는 없어지니까요!) null 검증에 대해서만큼은 코드가 장황해지는 비용보다 추후의 변경에 대한 비용이 더 크다고 생각하시는 것 같은데, 제 이해가 맞을까요?
-
미해결토비의 클린 스프링 - 도메인 모델 패턴과 헥사고날 아키텍처 Part 1
MemberRepositoryTest 중 Member table이 생성이 안됨
토비님 지금 강의 '엔티티 식별자와 JPA 엔티티' 섹션을 수강하던 중 MemberRepositoryTest에서 맴버 생성 테스트를 진행하는데 저는 Member 테이블이 생성이 안되어서 오류가 터지네요![Table "MEMBER" not found (this database is empty)토비님께서는 별 다른 설정 안하시고 해도 성공적으로 돌아갔는데 이유를 알 수 있을까요??
-
미해결자바 ORM 표준 JPA 프로그래밍 - 기본편
세타조인은 실무에서
내부조인과 외부조인이 아닌,join절을 사용하지않고 where절을 사용하는 세타조인도 실무에서 사용하나요? 성능적으로 안좋을거같은데, 혹시 어느 경우에 사용하는것일까요?
-
미해결토비의 클린 스프링 - 도메인 모델 패턴과 헥사고날 아키텍처 Part 1
compose.yml 파일 질문
안녕하세요 토비님처음 프로젝트를 실행하면 compose.yml 파일이services: mysql: image: 'mysql:latest' environment: - 'MYSQL_DATABASE=mydatabase' - 'MYSQL_PASSWORD=secret' - 'MYSQL_ROOT_PASSWORD=verysecret' - 'MYSQL_USER=myuser' ports: - '3306' 이렇게 설정되어있는데 저기서 데이터베이스 이름이나 user 정보를 변경하면 빌드가 되지않습니다.저 상태 그대로 놔두면 빌드는 되는데 인텔리제이의 데이터베이스 탭을 이용해서 접근해보면 splearn(제경우에는 이름을 변경하지 못했으니 아마 mydatabase) 데이터베이스가 생성되지 않고 Server Objects라는 폴더 하나만 존재합니다
-
미해결토비의 클린 스프링 - 도메인 모델 패턴과 헥사고날 아키텍처 Part 1
iterm 설정 문의
안녕하세요. 강의 잘 보고 있습니다.다름이 아니라 cat .sdkmanrc로 file을 출력할 때 줄 번호와 같이 이쁘게 정돈되어 출력되는 것을 보고 기능을 찾아보고 있는데 무엇인지 잘 모르겠어서 질문 글에서 여쭤봅니다.
-
해결됨실전! Querydsl
count 쿼리 관련 질문입니다!
[질문 내용]안녕하세요 영한님! 강의 잘 듣고 있습니다.수강 중 궁금한 점이 생겨 질문드립니다!스프링 데이터 페이징 활용1 - Querydsl 페이징 연동-> 이 강좌 수강 중에 생긴 궁금증입니다 count 쿼리 같은 경우는 아래와 같이 long 타입의 변수를 통해 저장되고 있습니다.long count = queryFactory .select(member.count()) .from(member) .leftJoin(member.team, team) .where( usernameEq(condition.getUsername()), teamNameEq(condition.getTeamName()), ageGoe(condition.getAgeGoe()), ageLoe(condition.getAgeLoe()) ) .fetchOne();위 조건은 저희가 쿼리를 통해 조회하는 데이터의 총 개수를 반환하는데, 그렇다면 아래와 같은 방식으로 총 개수를 구하면, count 쿼리를 굳이 사용할 필요 없이 1회의 쿼리만으로 PageImpl 객체를 반환할 수 있지 않을까? 라는 생각이 들었습니다. long size = result.size();이 size 기반 방식이 데이터 수가 적을 때 채용해볼 만한 방식인지, 아니면 count 쿼리를 사용했을 때,쿼리를 한 번 더 날릴 정도의 이점이 있는지 궁금합니다! 제 개인적인 생각입니다만 데이터 수가 특정 수치를 넘어가면 성능에 차이가 생기는 걸까요..?