inflearn logo
강의

강의

N
챌린지

챌린지

멘토링

멘토링

N
클립

클립

로드맵

로드맵

지식공유

묻고 답해요

172만명의 커뮤니티!! 함께 토론해봐요.

도메인 모델에서의 검증과 애플리케이션 레벨 검증의 경계

미해결

토비의 클린 스프링 - 도메인 모델 패턴과 헥사고날 아키텍처 Part 1

도메인 모델에서의 검증과 애플리케이션 레벨 검증의 경계에 대해 질문드립니다. 현재 도메인 모델에서는 이메일 형식이 올바른지, 닉네임이나 비밀번호가 null이 아닌지 같은 최소한의 조건만 검증하고 있습니다. 반면, 비밀번호가 8자 이상인지, 닉네임이 5자 이상인지 같은 검증은 애플리케이션 레이어에서 처리하고 있습니다. 그런데 닉네임이 5자 이상이어야 한다 같은 규칙도 도메인 규칙으로 볼 수 있지 않을까 라고 생각이 들어 해당 검증 역시 도메인 모델에서 처리하는게 맞지 않나 라는 생각이 드는데, 도메인 모델에서의 검증과 애플리케이션 레벨 검증의 경계는 어디까지 두는 게 좋은지 토비님 의견이 궁금합니다. 말씀하신것을 토대로 예측해 봤을때 형식이나 정책적 요구사항(변경 가능성이 있는 규칙)은 애플리케이션 레이어에서, 도메인의 본질적 불변 조건은 도메인에서 검증하는 걸까요?

  • java
  • spring
  • spring-boot
  • jpa
  • 리팩터링
  • ddd
dlwogns3413 댓글 2 좋아요 5 조회수 246

23년 2회 3회 JAVA 문제 18분26초

해결됨

(2026 최신!) 일주일만에 합격하는 정보처리기사 실기

compute(num-1) + compute(num-3) 여기서 출력값이 7-1=C(6) // 7-3=C(4) 6-1=C(5) // 4-1=C(3) 여기까진 알겠는데 풀이과정에 C(5) C(3) C(3) C(1) 이 파랑색 숫자는 어떻게 계산돼서 나온거죠?

  • python
  • java
  • c
  • 정보처리기사
최길로이 댓글 2 좋아요 0 조회수 62

23년 2회 C언어 킬러문제 33분47초 질문

해결됨

(2026 최신!) 일주일만에 합격하는 정보처리기사 실기

is what MAX_SIZE가 10인데 idx 0~7까지 적은 이유가 무엇인가요? 0~9까지 적어야 하는 거 아닌가요?

  • python
  • java
  • c
  • 정보처리기사
최길로이 댓글 2 좋아요 0 조회수 72

23년 2회 C언어 연산자 18분30초 질문

해결됨

(2026 최신!) 일주일만에 합격하는 정보처리기사 실기

switch 문인데 for문 안에 초기식 조건식 증감식이 있는데 for (int i = 0 ~~) { sum + =n[i]; <----- 요기를 건너띄고 바로 switch 로 가서 계산하는 이유가 무엇인지 궁금합니다.

  • python
  • java
  • c
  • 정보처리기사
최길로이 댓글 2 좋아요 0 조회수 49

querydsl 설정 문제

미해결

실전! Querydsl

학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요. 1. 강의 내용과 관련된 질문을 남겨주세요. 2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요. (자주 하는 질문 링크: https://bit.ly/3fX6ygx) 3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요. (질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG) 질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요. ========================================= [질문 템플릿] 1. 강의 내용과 관련된 질문인가요? (예/아니오) 예 2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오) 예 3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오) 예 [질문 내용] 여기에 질문 내용을 남겨주세요. auto plugins { 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하지 못하고 있는 상황입니다

  • java
  • jpa
  • querydsl
댓글 2 좋아요 0 조회수 247

동적쿼리를 이용한 List로 가져올 때의 캐쉬??

해결됨

6주 완성! 백엔드 이력서 차별화 전략 4가지 - 똑같은 이력서 속에서 돋보이는 법

제가 강의에서 이해한 바로는 example/1 과 같은 단일 리턴값에는 캐쉬를 적용해서 db의 부하를 줄일 수 있다고 생각했습니다. 하지만, search?title=딩코&tag=백엔드…” 와 같은 복합 검색 조건을 기반으로, 백만 건 이상의 데이터 중에서 동적 쿼리를 사용해 10개씩 페이지네이션하여 가져오고 있습니다. 이 경우에는 쿼리마다 값이 달라지니 미리 레디스에 값을 반영할 수도 없는 상황입니다. 그리고 DB에서는 10개의 값을 리턴시키는데, 이런 상황에서는 캐시를 사용할 수 없는 건가요? 반드시 인덱스를 통해서만 성능을 확보해야 하나요?

  • java
  • spring
  • mysql
  • redis
  • 이력서
kongminoo 댓글 2 좋아요 0 조회수 134

07:21-정적변수, 정적메서드(static): 설명이 조금 이상합니다

해결됨

(2026 최신!) 일주일만에 합격하는 정보처리기사 실기

아래 동일한 질문이 있어서 봤는데 그냥 암기하라고 하셔서 다시 질문드려봅니다. 일단 설명이 틀린 것 같은데 자세하게 다시한번 봐주세요. (7:12)(설명중)먼저 정적메서드에서 인스턴스변수를 접근하려고 한다 -> ??? 정적메서드에서 인스턴스메서드를 접근중인거 같은데요? 설명이 명확하지 않는 부분이 있어서 질문 드려봅니다

  • python
  • java
  • c
  • 정보처리기사
mclceo119 댓글 2 좋아요 0 조회수 115

Member#register() 메서드명이 모호하게 느껴집니다.

미해결

토비의 클린 스프링 - 도메인 모델 패턴과 헥사고날 아키텍처 Part 1

Member#create() 메서드를 register로 공통언어를 바꾸셨는데, 뜻이 모호해진 것 같아서 질문드립니다! 제가 생각하기에 register(등록)이라는 단어는 생성과 영속화라는 두가지의 행위를 함축한 단어로 느껴집니다. 따라서 MemberService#register()는 너무 자연스럽습니다. 실제로 Member를 생성한 후에 MemberRepository를 통하여 영속화까지 하는 내용으로 구현되어있습니다. 하지만 Member#register()는 이와 다르게 Java 객체를 생성하기만 하는 것이라, 메서드명과 실제 동작이 불일치한다고 느껴집니다. 하지만 또 동시에 도메인 모델을 글로 작성하는 과정에서 '멤버를 등록한다.' 라는 말을 쓰는 것은 자연스럽습니다. 그 구현이 코드적으로 Member에 있는 것만이 부자연스럽습니다. 이런 경우에는 Member라는 객체만으로 도메인 모델을 온전히 표현해내기가 어려운 것일까요? 해당 모델을 표현하기 위해서는 MemberService 같은 코드가 꼭 필요한 걸까요?

  • java
  • spring
  • spring-boot
  • jpa
  • 리팩터링
  • ddd
Dompoo 댓글 1 좋아요 0 조회수 230

조회수 redis 장애시 fallback 관련해서 질문!

해결됨

스프링부트로 직접 만들면서 배우는 대규모 시스템 설계 - 게시판

강사님 안녕하세요. 조회수 데이터를 Mysql로 백업한 데이터는 redis 장애시를 대비한 걸로 이해했습니다.(장애시 일부 데이터는 유실되지만, 가용성은 챙김) 그렇다면 조회수 조회시 ArticleViewService의 count메서드에서 try-catch로 묶어서 catch문에 mysql에서 조회수를 조회하는 코드를 추가하면 되는걸까요? 혹여나 강의 내용을 놓친거라면 죄송합니닷..!

  • java
  • mysql
  • spring-boot
  • redis
  • fallback
soap 댓글 2 좋아요 0 조회수 217

9:02초 질문있습니다.

해결됨

(2026 최신!) 일주일만에 합격하는 정보처리기사 실기

반대로 자식에게만 생성자가 있으면 오류인가요?? 질문은 동영상 강의에 들어가서 오른쪽에 커뮤니티 버튼을 통해 해주세요. 그래야 어떤 강좌에서 질문하셨는지 알 수 있습니다. 영상에서 몇 분 몇 초 대를 알려주셔야 제가 한 번에 질문 내용 확인이 가능합니다. 이미 다른 누군가가 질문을 한 이력 이 있을 수 있습니다. 질문 게시판을 한 번 확인 부탁드립니다. 학교의 과제나 타 강사의 코드 등 외부 수업 자료에 대해서는 답변하지 않습니다. 제가 다루는 커리큘럼 외의 이론이나, 너무 디테일한 컴퓨터 이론에 대해서는 답변 드리지 않습니다. 시험에 안 나오는 경우가 많고, 나와 봤자 1문제 나오는데 외워야 할 부분이 많은 것 등 (예시: 서브넷 마스크 계산) 질문을 올릴 때 이 글은 모두 지우고 내용을 입력해주세요.

  • python
  • java
  • c
  • 정보처리기사
이은비 댓글 2 좋아요 0 조회수 54

EntityManager#flush()를 검증하면 더 좋을 것 같습니다!

미해결

토비의 클린 스프링 - 도메인 모델 패턴과 헥사고날 아키텍처 Part 1

멤버를 저장하는 테스트에서 EntityManager#flush()를 호출해서 플러시되어 DB까지 쿼리문이 제대로 가는지 검증하시는 것을 보았습니다. (저는 플러시까지 테스트해볼 생각을 못했어서 아주 인상깊었습니다 ㅎㅎ) 하지만 해당 부분이 검증문 없이 홀로 호출되고 있습니다. 좀더 의도를 드러내기 위해서는 assertThatCode(() -> em.flush()).doesNotThrowAnyException() 처럼 코드를 작성하는게 더 좋지 않을까 싶어서 질문드립니다! 적용 예시 @Test void 멤버를_저장한다() { var member = MemberFixture.createAny(); memberRepository.save(member); assertThat(member.id()).isNotNull(); assertThatCode(() -> em.flush()) .doesNotThrowAnyException(); }

  • java
  • spring
  • spring-boot
  • jpa
  • 리팩토링
  • ddd
Dompoo 댓글 2 좋아요 0 조회수 156

테스트클래스명이 테스트 목적을 잘 나타내지 못하는 것 같습니다.

미해결

토비의 클린 스프링 - 도메인 모델 패턴과 헥사고날 아키텍처 Part 1

MemberRepositoryTest는 EntityManager 나 @DataJpaTest 등 Jpa와 깊게 관련되어있는 테스트로 이해됐습니다. EntityManager#flush() 가 잘 호출되는지 확인하는 부분도 그런 이유에서 이해됩니다. 하지만 테스트명이 MemberRepositoryTest인 점에서 헷갈리는 부분이 발생합니다. MemberRepository 인터페이스의 메서드를 테스트하는 것이 목적이라면 좀 더 추상화된 테스트를 작성하는 것이 옳지 않았나? 라는 것이 주된 궁금증입니다. 물론 Spring Data JPA 특성상 런타임에 구현체가 만들어지기 때문에, 테스트 대상은 인터페이스를 사용해야 한다는 것은 이해합니다만, 최소한 클래스명은 좀 더 구체적으로 작성해주는 것이 의도에 부합하지 않나? 해서 질문드립니다! 어떻게 생각하시나요?

  • java
  • spring
  • spring-boot
  • jpa
  • 리팩토링
  • ddd
Dompoo 댓글 2 좋아요 0 조회수 168

도메인 로직으로 분리해도 되나요?

미해결

토비의 클린 스프링 - 도메인 모델 패턴과 헥사고날 아키텍처 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); } }

  • java
  • spring
  • spring-boot
  • jpa
  • 리팩토링
  • ddd
문수 댓글 1 좋아요 1 조회수 209

JobParameter의 JSON 표기법 명령어 오류

미해결

죽음의 Spring Batch: 새벽 3시의 처절한 공포는 이제 끝이다.

JSON 표기법 실습을 진행중이다. 아래 명령어를 실행하는데, 에러가 난다. json 문자열 속 쌍따옴표를 백슬래시로 이스케이프 하는게 안 먹혀서 그런 것 같다. 견문이 부족하여 스스로 해결 못 하였다. GPT가 알려주는 대로도 해봤지만 따옴표가 문자열에 들어가지 않는다. 문자열이 어떻게 입력되는지는 아래에 있는 에러코드에서 확인 가능하다. 혹시나 답을 알 수 있을까 질문한다. window 환경, 스프링부트 3.5.2, jdk 17이고, 인텔리제이에서 실습중이다. intellij 로컬 터미널과, git bash등에서 아래 명령어를 실행했으나 실패하였다. ./gradlew bootRun --args="--spring.batch.job.name=terminatorJob infiltrationTargets='{\"value\":\"판교서버실,안산데이터센터\",\"type\":\"java.lang.String\"}'" ./gradlew bootRun --args="--spring.batch.job.name=terminatorJob infiltrationTargets={\"value\":\"판교_서버실,안산_데이터센터\",\"type\":\"java.lang.String\"}" package com.system.batch.killbatchsystem.JobParameter; import lombok.extern.slf4j.Slf4j; import org.springframework.batch.core.Job; import org.springframework.batch.core.Step; import org.springframework.batch.core.configuration.annotation.StepScope; import org.springframework.batch.core.converter.JobParametersConverter; import org.springframework.batch.core.converter.JsonJobParametersConverter; import org.springframework.batch.core.job.builder.JobBuilder; import org.springframework.batch.core.repository.JobRepository; import org.springframework.batch.core.step.builder.StepBuilder; import org.springframework.batch.core.step.tasklet.Tasklet; import org.springframework.batch.repeat.RepeatStatus; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.stereotype.Component; import org.springframework.transaction.PlatformTransactionManager; @Configuration @Slf4j public class SystemTerminatorConfig { @Bean public Job processTerminatorJob(JobRepository jobRepository, Step terminationStep){ return new JobBuilder("terminatorJob", jobRepository) .start(terminationStep) .build(); } @Bean public Step terminationStep(JobRepository jobRepository, PlatformTransactionManager transactionManager, Tasklet terminatorTasklet ){ return new StepBuilder("terminationStep", jobRepository) .tasklet(terminatorTasklet, transactionManager) .build(); } // @Bean // @StepScope // public Tasklet terminatorTasklet(@Value("#{jobParameters['terminatorId']}")String terminatorId, // @Value("#{jobParameters['targetCount']}") Integer targetCount){ // return (contribution, chunkContext) -> { // log.info("시스템 종결자 정보:"); // log.info("ID: {}", terminatorId); // log.info("제거 대상 수: {}", targetCount); // log.info("🎇 SYSTEM TERMINATOR {} 작전을 개시합니다", terminatorId); // log.info("👻 {}개의 프로세스를 종료합니다.", targetCount); // // for(int i =1; i<=targetCount; i++){ // log.info("👻 프로세스 {} 종료 완료!", i); // } // // log.info("🎯 임무 완료: 모든 대상 프로세스가 종료되었습니다."); // return RepeatStatus.FINISHED; // }; // } @Bean @StepScope public Tasklet terminatorTasklet( @Value("#{jobParameters['infiltrationTargets']}") String infiltrationTargets ) { return (contribution, chunkContext) -> { String[] targets = infiltrationTargets.split(","); log.info("⚡ 침투 작전 개시"); log.info("첫 번째 타겟: {} 침투 시작", targets[0]); log.info("마지막 타겟: {} 에서 집결", targets[1]); log.info("🎯 임무 전달 완료"); return RepeatStatus.FINISHED; }; } @Bean public JobParametersConverter jobParameterConverter() { return new JsonJobParametersConverter(); } } 오류 내용(명령어를 intellij 로컬 터미널에서 실행) PS C:\Users\Desktop\study\스프링배치\kill-batch-system> ./gradlew bootRun --args="--spring.batch.job.name=terminatorJob infiltrationTargets={\"value\":\"판교서버실,안산데이터센터\",\"type\":\"java.lang.String\"}" . ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v3.5.2) 2025-06-23T10:10:06.623+09:00 INFO 36520 --- [kill-batch-system] [ main] c.s.b.k.KillBatchSystemApplication : Starting KillBatchSystemApplication using Java 17.0.15 with PID 36520 (C:\Users\Bistelligence\Desktop\study\스프링배치\kill-batch-system\build\classes\java\main started by Bistelligence in C:\Users\Bistelligence\Desktop\study\스프링배치\kill-batch-system) 2025-06-23T10:10:06.625+09:00 INFO 36520 --- [kill-batch-system] [ main] c.s.b.k.KillBatchSystemApplication : No active profile set, falling back to 1 default profile: "default" 2025-06-23T10:10:07.436+09:00 INFO 36520 --- [kill-batch-system] [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting... 2025-06-23T10:10:07.577+09:00 INFO 36520 --- [kill-batch-system] [ main] com.zaxxer.hikari.pool.HikariPool : HikariPool-1 - Added connection conn0: url=jdbc:h2:mem:bc0bd0fe-d717-4e43-908b-4ee615d3746b user=SA 2025-06-23T10:10:07.579+09:00 INFO 36520 --- [kill-batch-system] [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed. 2025-06-23T10:10:07.834+09:00 INFO 36520 --- [kill-batch-system] [ main] c.s.b.k.KillBatchSystemApplication : Started KillBatchSystemApplication in 1.634 seconds (process running for 1.953) 2025-06-23T10:10:07.836+09:00 INFO 36520 --- [kill-batch-system] [ main] o.s.b.a.b.JobLauncherApplicationRunner : Running default command line with: [infiltrationTargets={\value\:\판교서버실,안산데이터센터\,\type\:\java.lang.String\}] 2025-06-23T10:10:07.884+09:00 INFO 36520 --- [kill-batch-system] [ main] .s.b.a.l.ConditionEvaluationReportLogger : Error starting ApplicationContext. To display the condition evaluation report re-run your application with 'debug' enabled. 2025-06-23T10:10:07.898+09:00 ERROR 36520 --- [kill-batch-system] [ main] o.s.boot.SpringApplication : Application run failed org.springframework.batch.core.converter.JobParametersConversionException: Unable to decode job parameter {\value\:\판교서버실,안산데이터센터\,\type\:\java.lang.String\} at org.springframework.batch.core.converter.JsonJobParametersConverter.decode(JsonJobParametersConverter.java:121) ~[spring-batch-core-5.2.2.jar:5.2.2] at org.springframework.batch.core.converter.DefaultJobParametersConverter.getJob ~~~~ ~~~~ Git Bash에서 명령어를 실행할 경우 데이터는 아래처럼 입력되었다. org.springframework.batch.core.converter.JobParametersConversionException: Unable to decode job parameter {value:판교_서버실,안산_데이터센터,type:java.lang.String} ~~~~ > Task :bootRun FAILED FAILURE: Build failed with an exception. * What went wrong: Execution failed for task ':bootRun'. > Process 'command 'C:\Program Files\Eclipse Adoptium\jdk-17.0.15.6-hotspot\bin\java.exe'' finished with non-zero exit value 1 * Try: > Run with --stacktrace option to get the stack trace. > Run with --info or --debug option to get more log output. > Run with --scan to get full insights. > Get more help at https://help.gradle.org. BUILD FAILED in 3s

  • java
  • spring
  • spring-boot
  • spring-batch
  • backend
양윤석 댓글 5 좋아요 1 조회수 316

AsyncExcutor 설정값 질문

해결됨

서버개발자 과제전형 완벽가이드 - 1편

안녕하십니까 강사님. 강의를 잘 듣고있던 중에 질문이 생겨서 드립니다. AsyncExcutor의 설정값을 설정하는 부분에서 서비스 앞단에서 대고객 트래픽을 받을때 maxPoolSize를 무한으로 설정하고, 큐의 크기를 0으로 두어서 빠르게 스레드를 생성하여 요청을 처리한다고 하셨습니다. 또한 그 설정을 한 스레드 풀의 작업들은 대체로 레디스 캐싱된 값을 뿌려주는것과 같이 응답시간이 빠르게 보장된 요청의 경우에 위와같은 설정값을 설정한다고 이해했습니다. 실제 실무에서 위와같이 설정해도 무한대까지 스레드가 생기지는 않으니 문제가 없다?고 하신것 같은데 그 이유가 잘 이해가 안되는 것 같습니다. 제 나름대로 추론을 해봤는데요. 우선 redis에 캐싱된 결과를 뿌리는 요청을 예로 들어서 보면, I/O 바운드 작업이므로 스레드가 빠르게 증가하여 요청을 처리하는게 중요하고, 스레드가 증가하더라고 CPU 사용량의 증가가 CPU 바운드 작업처럼 크게 증가하지 않을것이다. 따라서 스파이크 트래픽이 발생해도 서버가 죽거나 문제가 생기지는 않을것이다. 이렇게 생각을 해봤는데 좀 더 정확한 답변을 듣고싶어서 질문드립니다!

  • java
  • spring
  • spring-boot
  • database
파이리이2 댓글 2 좋아요 0 조회수 112

도메인에 대한 개인적인 경험이 다음 설계에 영향을 주는 경우가 많습니다. 토비님께서는 설계를 하실 때, 이전 경험에서 비롯된 도메인을 어느 정도까지 설계에 반영하시나요?

미해결

토비의 클린 스프링 - 도메인 모델 패턴과 헥사고날 아키텍처 Part 1

안녕하세요, 토비님. 주니어로서 아직 부족한 점이 많지만, 궁금한 점이 있어 또 이렇게 질문드립니다. Section 3에서 소개된 splearn 도메인 강의를 듣기 전에, 스스로 먼저 도메인 모델링을 해보는 시간을 가졌습니다. 그 과정에서 회원 , 수강 , 강의 등의 개념 외에 수강신청 이라는 도메인을 도출했는데요, 그 이유는 “수강은 수강신청 후 결제가 완료되어야 성립된다” 는 제 안의 암묵적인 경험에 의한 도메인 해석 때문이었습니다. 그런데 사실 강의에서 제시된 사용자 스토리에는 결제 라는 개념이 존재하지 않았습니다. 그럼에도 불구하고, 과거 경험에서 비롯된 도메인이 자연스럽게 설계에 스며들었고, 결과적으로 오버엔지니어링으로 이어졌다 는 생각이 들었습니다. 이런 식의 ‘의도되지 않았지만 그럴듯한 도메인’ 을 설계에 넣고 싶은 유혹은 실제 회사에서 설계할 때도 자주 느끼는 부분입니다. 그래서 토비님께서는 이런 상황에서 어떤 기준으로 도메인을 포함하거나 배제하시는지, 그리고 본인의 경험을 어떤 방식으로 설계에 녹이시는지 가 궁금해졌습니다. 바쁘시겠지만, 짧은 의견이라도 들려주시면 큰 도움이 될 것 같습니다. 감사합니다!

  • java
  • spring
  • spring-boot
  • jpa
  • 리팩토링
  • ddd
종운 댓글 2 좋아요 1 조회수 236

인터페이스 위치를 결정하는 기준에 관해

미해결

토비의 클린 스프링 - 도메인 모델 패턴과 헥사고날 아키텍처 Part 1

안녕하세요 토비님, 강의 너무 재미있게 듣고 있습니다. 인터페이스를 도메인 계층에 둘지, required 포트에 둘지 결정하는 기준이 있을까요? Splearn의 코드를 예로 들어보면, 도메인 서비스 인터페이스인 passwordEncoder 는 어댑터 계층에서 구체적인 기술로 구현된다는 점에서 EmailSender , MemberRepository 같은 required 포트의 인터페이스와 유사하게 느껴집니다. 이들 모두 외부 세계(또는 특정 기술)와 애플리케이션을 분리하는 역할을 하기 때문입니다. 만약 애플리케이션 서비스에서 passwordEncoder 를 사용해 비밀번호를 암호화한 후, 암호화된 비밀번호( passwordHash )를 Member.register 메서드에 인자로 직접 넘겨준다면, passwordEncoder 역시 required 포트에 위치할 수 있지 않을까요? 도메인 계층도 애플리케이션 서비스에 의존하지 않게 되고요. 그런데도 불구하고 passwordEncoder 를 도메인 계층의 인터페이스로 두신 이유가 궁금합니다. 감사합니다.

  • java
  • spring
  • spring-boot
  • jpa
  • 리팩토링
  • ddd
Speedwagon 댓글 2 좋아요 1 조회수 313

build 폴더 생성 관련 문의

미해결

옆집 개발자와 같이 진짜 이해하며 만들어보는 첫 Spring Boot 프로젝트

폴더 똑같이 생성하고, build.gradle 파일로 프로젝트 오픈 후 build successful 되었는데도 build 폴더만 생성되지 않았습니다. 이 경우 그냥 진행해도 될까요?

  • java
  • spring
  • rest-api
  • spring-boot
  • dbms/rdbms
댓글 1 좋아요 2 조회수 87

record 생성 시 휴먼 에러 발생 가능성

미해결

토비의 클린 스프링 - 도메인 모델 패턴과 헥사고날 아키텍처 Part 1

안녕하세요, 토비님. 메서드 파라미터가 많아질 경우 record 타입의 parameter object를 사용하여 가독성을 높일 수 있는 방법을 소개해주셨는데요, 이 방법으로도 parameter object 인스턴스 생성 시 argument를 잘못 전달한다거나 등의 휴먼 에러는 여전히 발생 가능하지 않나 생각이 듭니다. java record도 kotlin의 named parameter와 같은 기능을 제공하면 좋겠지만 검색해봤을땐 아직 제공하지 않는 것 같더라구요. 이처럼 argument 전달 버그는 예방할 방법이 없을까요? 혹은 강의 뒷부분에서 이러한 점을 개선해주실까요? record에 builder를 사용해야 하나 생각이 들긴 하는데, 괜찮은 방법인지는 물음표네요.

  • java
  • spring
  • spring-boot
  • jpa
  • 리팩토링
  • ddd
대흉근 댓글 3 좋아요 1 조회수 255

영한님, sleep(10) 이유가 궁금합니다.

미해결

김영한의 실전 자바 - 고급 1편, 멀티스레드와 동시성

안녕하세요. 영한님 궁금한 점이 있습니다. sleep(10) 를 하면 동시 실행을 더 촉진할 수 있다는 것이 이해가 안됩니다. 예를 들어, 아래를 가정한다면요! 각 시간은 별도로 가정한 것입니다. 1번 쓰레드가 1.0초에 먼저 실행이 된 후 0.1 초 쉬었다가 실행이 되면 1.1초에 실행 가정, 2번 쓰레드가 1.1초에 먼저 실행이 된 후 0.1 초 쉬었다가 실행이 되면 1.2초에 실행 가정, 3번 쓰레드가 1.2초에 먼저 실행이 된 후 0.1 초 쉬었다가 실행이 되면 1.3초에 실행 가정, ... 그렇다면 각 쓰레드가 0.1초씩 쉬었다고 하더라도 결국에는 실행시점이 1.1초, 1.2초, 1.3초 다 다르다고 봐도 되지 않나요? 뭔가, sleep(10) 을 빠지게 되면 대부분 쓰레들이 비슷한 시점에 10ms 를 자고, 거의 동시에 깨어나서 increment() 를 실행하는 것 같은데 맞나요? 맞다면 왜 그런가요? 감사합니다!

  • java
  • 객체지향
  • 동시성
  • multithread
  • thread
highjune 댓글 2 좋아요 0 조회수 124

인기 태그

인프런 TOP Writers

주간 인기글