안녕하세요. Member와 MemberDetail의 연관관계 주인이 바뀐게 아닌가 싶습니다. 비록 1:1 관계이고, 두 객체 인스턴스가 동시에 생성되고 테이블에 영속화 되게끔 설정된 거는 맞지만, 논리적으로 Member 엔터티가 상위 엔터티이고, MemberDetail이 하위 엔터티가 맞는 것 같아요. 추후 Member 엔터티를 참조하는 다른 엔터티가 만들어질텐데, MemberDetail을 참조하는게 아니라 Member를 참조해야 하고요. 관련한 의견 부탁 드립니다. 감사합니다.
안녕하세요. 도메인 영역에 @Entity, @Query 애너테이션을 사용하는게 문제가 없다는 내용과 이유를 강의에서도, 질문&답변에서도 잘 설명해 주셨고, 모두 확인했습니다. 동일한 이유로, 강의에서 DuplicateEmailException에 @ResponseStatus 애너테이션 사용 시, 의존성 문제에 대해서 언급한 부분도, JPA 애너테이션을 사용할 수 있다는 동일한 근거로 허용되어도 문제가 없는게 아닐까요? 물론, @ResponseStatus 사용 시에 상태 코드 외에 추가적인 메시지 설정이 불가능하다는 등 단점이 있어서 사용하지는 않겠지만요. 그저 기술 의존성 침투 관점에서 궁금해서 문의 드립니다. 감사합니다.
안녕하세요 강사님 좋은 강의 잘 듣고 있습니다 강의에서 말씀하신 그레이스풀 셧다운과 데몬스레드의 관계까 제가 이해한 의미와 조금 달라서 여쭤봅니다. 저는 그레이스풀 셧다운을 진행 중인 작업을 마무리하고 안전하게 종료하는 것으로 이해하고 있는데 데몬 스레드는 JVM 종료 시 작업이 중간에 끊길 수도 있다고 알고 있습니다. 그래서 “레디스 이벤트 처리 과정이 백그라운드에서 알아서 들어가기 때문에 데몬 스레드를 사용하면 자연스럽게 그레이스풀 셧다운이 된다”라는 설명에서 레디스의 백그라운드 이벤트 처리 방식과 데몬 스레드 사용이 어떤 식으로 연결되는지를 조금 더 구체적으로 알고 싶습니다. 또, 메시지를 소비하는 도중에 애플리케이션이 종료된다면 메시지 손실 가능성은 없는지도 궁금합니다. 그리고 실무 환경에서도 보통 이런 방식(데몬 스레드 기반)으로 Redis Pub/Sub 리스너를 구성하는지 아니면 다른 종료 처리 방식을 더 선호하는지도 알고 싶습니다. 좋은 강의 잘 듣고있습니다! 감사합니다.
자바와 스프링 부트로 생애 최초 서버 만들기, 누구나 쉽게 개발부터 배포까지! [서버 개발 올인원 패키지]
생성자에서 필드에 의존성 주입하면 예를 들어 컨트롤러 생성자를 만들때 서비스 필드에 의존성 주입해주면 그때는 @Autowired가 자동으로 존재한다고 했는데 이 @Autowired 는 생성자의 파라미터를 전부 빈으로 등록시켜주는건가요? 만약 그렇다고 한다면 생성자의 파라미터가 객체가 아닌 경우는 어떻게 되는건가요? 혹시 객체 타입 파라미터만 빈으로 등록시켜주고 객체가 아닌 파라미터는 빈으로 등록시켜주지 않나요?
안녕하세요. 강의를 듣다 아래와 같은 내용에 대해 개인적인 생각이 있고, 이 생각에 대해 강사님의 생각이 궁금하여 질문 글 올립니다. "오해: 단위 테스트와 통합 테스트를 잘 분리해야한다." 저 또한, 단위 테스트와 통합 테스트를 분명하게 구분하기에는 모호한 부분이 있다고 생각합니다. 하지만, 저는 이를 분리해서 테스트를 진행하고자 합니다. 명확한 합의는 없으나, 팀 내 혹은 저에게 있어 단위 테스트와 통합 테스트를 아래와 같이 구분하고자 했습니다. 단위 테스트 : 외부의 영향이 존재 하지 않고 가장 작은 단위로써 해당 부분 내에서만 테스트를 진행. 통합 테스트 : 외부의 영향까지 반영하여 테스트. 스프링부트 테스트, JPA 테스트 등 이렇게 분리해서 테스트를 진행하고자 하는 이유는 다음과 같습니다. 테스트에 대한 빠른 피드백 테스트 할 때 빠른 피드백과 빠른 수정이 필수라고 생각합니다. 단위 테스트의 경우 외부 영향 없이, mock을 통해 빠르게 내가 목록화한 테스트를 진행하며, CI 환경에서도 빠르게 피드백을 받을 수 있습니다. 반면, 통합 테스트의 경우 비교적 시간 소비가 많이 됩니다. 스프링부트 테스트의 경우 비교적 적은 시간이 소비되지만, JPA 테스트와 같이 디비에 대한 테스트 혹은 다른 플랫폼( 예 : 키클락 등)을 진행하기 위해서는 테스트 컨테이너와 같은 도구를 활용할 수 있다고 생각합니다. 이러한 도구를 사용함에 있어서 많은 시간이 소비가 될 가능성이 크다고 생각합니다. 특히 저희 회사의 경우 테스트 환경이 많이 좋지 않아 시간이 훨씬 더 많이 걸리게 됩니다. 그래서 저는 단위 테스트랑 통합 테스트를 분리하여 개발 환경에서는 단위 테스트를, 운영 CI 환경에서는 단위 + 통합을 함께하여 테스트를 진행하고 있습니다. 이렇게 분리해서 진행하는 것은 나름 장점이 보이고 있다고 생각하는데 이 부분에 대해 강사님의 의견과 잘못된 부분이 있다면 피드백 부탁드립니다.
Kotlin, Groovy gradle, PostrgreSQL 기반으로 프로젝트 세팅이 되어 있습니다 @Repository class ActorRepository( private val dsl: DSLContext, configuration: Configuration ) { private val actorDao = ActorDao(configuration) companion object { private val ACTOR = JActor.ACTOR } fun save(actor: Actor): Unit = actorDao.insert(actor) } 이러한 방식으로 Repository 가 구현되어 있을 때, @SpringBootTest class ActorRepositoryTest( private val actorRepository: ActorRepository, ): StringSpec({ "insert test" { val actor = Actor().apply { firstName = "John" lastName = "Doe" } println("Actor before insert: $actor") val insertedActor = actorRepository.save(actor) } }) { override fun extensions() = listOf(SpringExtension) } 위 쿼리가 실행됩니다 제가 예상했던 insert into "actor" ("first_name", "last_name") values(?, ?) 과는 다르게 id 값이 0으로 고정되어 생성되더라구요 generate dao 를 생성하는 방법이 잘못된 걸까요?
강사님 안녕하세요. 게시판 조회시 페이징으로 처리할때 조회수를 오름차순, 내림차순 정렬하는 기능이 있다면 현재 강의 설계 기준으로 어떻게 구현할 수 있을까요? 제가 생각한 방법은 다음과 같습니다. 사전 조건: view에서 조회수 데이터는 redis를 바라봄 개수 백업 적용 / 단위는 10 페이징 쿼리에서 RDB 백업 테이블의 조회수 기준으로 정렬 코드 레벨에서 redis 데이터 기준으로 한번 더 정렬 문제는, 2번에서 한 페이지의 데이터만 정렬이 되기 대문에 페이지를 넘어갈때 정렬이 깨지는 현상이 발생합니다 ㅠ 데이터 전체를 메모리에 한번에 조회하지 않고 할 수 있는 방법이 있을까요?
학습중 궁금한 것은 언제든 문의 하세요. 질문을 최대한 자세히 남겨주시면 반드시 답변 드리도록 하겠습니다. 추가로 알고 싶은 내용도 요청해주시면 강의 자료를 업데이트 해서 제공할 예정입니다. application.yml spring: application: name: HelloMessageQueue rabbitmq: host: localhost port: 5672 username: admin password: admin server: port: 8080 docker-compose.yml services: rabbitmq: image: rabbitmq:3-management container_name: mq ports: - "5672:5672" - "15672:15672" environment: RABBITMQ_ERLANG_COOKIE: rabbitmqCookie RABBITMQ_DEFAULT_USER: admin RABBITMQ_DEFAULT_PASS: admin 이렇게 해서 실행하면 2025-08-08T11:17:15.145+09:00 ERROR 11684 --- [HelloMessageQueue] [ restartedMain] o.s.boot.SpringApplication : Application run failed org.springframework.context.ApplicationContextException: Failed to start bean 'container' at org.springframework.context.support .DefaultLifecycleProcessor.doStart(DefaultLifecycleProcessor.java:408) ~[spring-context-6.2.9.jar:6.2.9] at org.springframework.context.support .DefaultLifecycleProcessor.doStart(DefaultLifecycleProcessor.java:394) ~[spring-context-6.2.9.jar:6.2.9] at org.springframework.context.support .DefaultLifecycleProcessor$LifecycleGroup.start(DefaultLifecycleProcessor.java:586) ~[spring-context-6.2.9.jar:6.2.9] at java.base/java.lang.Iterable.forEach(Iterable.java:75) ~[na:na] at org.springframework.context.support .DefaultLifecycleProcessor.startBeans(DefaultLifecycleProcessor.java:364) ~[spring-context-6.2.9.jar:6.2.9] at org.springframework.context.support .DefaultLifecycleProcessor.onRefresh(DefaultLifecycleProcessor.java:310) ~[spring-context-6.2.9.jar:6.2.9] at org.springframework.context.support .AbstractApplicationContext.finishRefresh(AbstractApplicationContext.java:1006) ~[spring-context-6.2.9.jar:6.2.9] at org.springframework.context.support .AbstractApplicationContext.refresh(AbstractApplicationContext.java:630) ~[spring-context-6.2.9.jar:6.2.9] at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146) ~[spring-boot-3.5.4.jar:3.5.4] at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:752) ~[spring-boot-3.5.4.jar:3.5.4] at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:439) ~[spring-boot-3.5.4.jar:3.5.4] at org.springframework.boot.SpringApplication.run (SpringApplication.java:318) ~[spring-boot-3.5.4.jar:3.5.4] at org.springframework.boot.SpringApplication.run (SpringApplication.java:1361) ~[spring-boot-3.5.4.jar:3.5.4] at org.springframework.boot.SpringApplication.run (SpringApplication.java:1350) ~[spring-boot-3.5.4.jar:3.5.4] at com.study .rabbitmq.RabbitmqApplication.main(RabbitmqApplication.java:10) ~[main/:na] at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na] at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[na:na] at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na] at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na] at org.springframework.boot.devtools.restart.RestartLauncher.run (RestartLauncher.java:50) ~[spring-boot-devtools-3.5.4.jar:3.5.4] Caused by: org.springframework.amqp.AmqpIllegalStateException: Fatal exception on listener startup at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.waitForConsumersToStart(SimpleMessageListenerContainer.java:643) ~[spring-rabbit-3.2.6.jar:3.2.6] at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.doStart(SimpleMessageListenerContainer.java:603) ~[spring-rabbit-3.2.6.jar:3.2.6] at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.start(AbstractMessageListenerContainer.java:1417) ~[spring-rabbit-3.2.6.jar:3.2.6] at org.springframework.context.support .DefaultLifecycleProcessor.doStart(DefaultLifecycleProcessor.java:405) ~[spring-context-6.2.9.jar:6.2.9] ... 19 common frames omitted Caused by: org.springframework.amqp.rabbit.listener.exception.FatalListenerStartupException: Authentication failure at org.springframework.amqp.rabbit.listener.BlockingQueueConsumer.start(BlockingQueueConsumer.java:631) ~[spring-rabbit-3.2.6.jar:3.2.6] at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer$AsyncMessageProcessingConsumer.initialize(SimpleMessageListenerContainer.java:1482) ~[spring-rabbit-3.2.6.jar:3.2.6] at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer$ AsyncMessageProcessingConsumer.run (SimpleMessageListenerContainer.java:1322) ~[spring-rabbit-3.2.6.jar:3.2.6] at java.base/ java.lang.Thread.run (Thread.java:842) ~[na:na] Caused by: org.springframework.amqp.AmqpAuthenticationException: com.rabbitmq.client.AuthenticationFailureException: ACCESS_REFUSED - Login was refused using authentication mechanism PLAIN. For details see the broker logfile. at org.springframework.amqp.rabbit.support .RabbitExceptionTranslator.convertRabbitAccessException(RabbitExceptionTranslator.java:64) ~[spring-rabbit-3.2.6.jar:3.2.6] at org.springframework.amqp.rabbit.connection.AbstractConnectionFactory.createBareConnection(AbstractConnectionFactory.java:632) ~[spring-rabbit-3.2.6.jar:3.2.6] at org.springframework.amqp.rabbit.connection.CachingConnectionFactory.createConnection(CachingConnectionFactory.java:726) ~[spring-rabbit-3.2.6.jar:3.2.6] at org.springframework.amqp.rabbit.connection.ConnectionFactoryUtils.createConnection(ConnectionFactoryUtils.java:257) ~[spring-rabbit-3.2.6.jar:3.2.6] at org.springframework.amqp.rabbit.connection.ConnectionFactoryUtils$RabbitResourceFactory.createConnection(ConnectionFactoryUtils.java:345) ~[spring-rabbit-3.2.6.jar:3.2.6] at org.springframework.amqp.rabbit.connection.ConnectionFactoryUtils.doGetTransactionalResourceHolder(ConnectionFactoryUtils.java:140) ~[spring-rabbit-3.2.6.jar:3.2.6] at org.springframework.amqp.rabbit.connection.ConnectionFactoryUtils.getTransactionalResourceHolder(ConnectionFactoryUtils.java:102) ~[spring-rabbit-3.2.6.jar:3.2.6] at org.springframework.amqp.rabbit.connection.ConnectionFactoryUtils.getTransactionalResourceHolder(ConnectionFactoryUtils.java:85) ~[spring-rabbit-3.2.6.jar:3.2.6] at org.springframework.amqp.rabbit.listener.BlockingQueueConsumer.start(BlockingQueueConsumer.java:625) ~[spring-rabbit-3.2.6.jar:3.2.6] ... 3 common frames omitted Caused by: com.rabbitmq.client.AuthenticationFailureException: ACCESS_REFUSED - Login was refused using authentication mechanism PLAIN. For details see the broker logfile. at com.rabbitmq.client.impl.AMQConnection.start(AMQConnection.java:395) ~[amqp-client-5.25.0.jar:5.25.0] at com.rabbitmq.client.ConnectionFactory.newConnection(ConnectionFactory.java:1251) ~[amqp-client-5.25.0.jar:5.25.0] at com.rabbitmq.client.ConnectionFactory.newConnection(ConnectionFactory.java:1198) ~[amqp-client-5.25.0.jar:5.25.0] at org.springframework.amqp.rabbit.connection.AbstractConnectionFactory.connectAddresses(AbstractConnectionFactory.java:678) ~[spring-rabbit-3.2.6.jar:3.2.6] at org.springframework.amqp.rabbit.connection.AbstractConnectionFactory.connect(AbstractConnectionFactory.java:647) ~[spring-rabbit-3.2.6.jar:3.2.6] at org.springframework.amqp.rabbit.connection.AbstractConnectionFactory.createBareConnection(AbstractConnectionFactory.java:593) ~[spring-rabbit-3.2.6.jar:3.2.6] ... 10 common frames omitted 이 에러가 발생합니다 계정도 있는데 인증이 안되는 이유를 모르겠습니다
안녕하세요. 양질의 강의 덕분에 제한된 시야에서 새로운 인사이트를 얻을 수 있었습니다. 섹션 18 - 안정감 있는 아키텍처 개선 챕터를 수강하고 있으며, 현재까지 강의를 들으면서 궁금 했던 내용 두 가지를 여쭈어보고 싶습니다. 질문 1. " TDD를 사용해 만들어진 먹구름 아키텍처를 어느 시점에서 개선 해야 할까요? " 질문을 하게 된 계기는 전형적으로 많이 사용되는 레이어드 아키텍처를 첫 기능 구현 단계에서 부터 차용하여 개발을 진행 해왔는데, 왜 이 아키텍처를 사용했을까? 란 의문이 없었거든요. 강의에서는 흔히 말해 스마트 UI 패턴으로 요구사항을 충분히 충족 해오며 점진적으로 개선 해왔습니다. 그러다 모종의 이유로 내부 아키텍처를 개선 하면서 진행이 되었는데, 아키텍처를 개선 해야겠다고 느끼는 요소가 무엇인지 궁금합니다. 요구사항이 추가 되었을 때 개발 비용을 최소화 하여 충족 시킬 수 있는가? 라고 생각을 하고 있는데, 현재 까지 불편함을 느낄 정도의 확장성을 강요 받아본 적 없어서 감을 못 잡는 것 같습니다. 질문 2. " 어떤 영역을 대상으로 테스트를 작성해야할까? " - 테스트 코드 작성 범위 고민 위 질문 내용과 답변을 보고 무언가 감이 오는 듯 하나, 아직 정리되지 않는 느낌입니다. API 계층의 동작이 가볍다 또는 무겁다의 판단 기준 API 계층의 동작이 가벼운지 무거운지에 대한 판단은 어떻게 하시는지 궁금합니다. 해당 강의에선 API 계층의 동작이 가벼웠기 때문에 API 스펙에 인수테스트를 진행 한 것 같습니다. 서비스 대상으로 테스트를 많이 실행하고, API 는 간단한 성공 경우에 테스트를 한다 . 이 케이스에선 아래와 같은 테스트 시나리오가 있다고 가정한다면 테스트를 어떻게 하면 좋을까요? - username 속성이 null 이거나 공백이라면 HasUsernameNullOrEmpty 예외를 반환한다 - username 속성이 올바른 형식을 따르지 않으면 InvalidUsername 예외를 반환한다 - username 속성이 올바른 형식을 따르면 User 객체를 생성한다 서비스 테스트에서 위 3가지 케이스에 대해 검증 인수 테스트에서 username 을 정확히 입력한 경우 상태코드와 유저에 대한 정보를 반환 하는지 검증 이렇게 테스트를 구성한다고 이해 했는데 제대로 이해한걸까요? 다시 한 번 질문을 빌미로 감사 인사를 드리면서 고견 부탁드리겠습니다. 항상 건강하세요!
📤 before request jwtUtil.ts:30 ✅ Response Received jwtUtil.ts:34 📦 Content-Type: application/json jwtUtil.ts:35 🧾 typeof res.data : string content-type은 json 형식으로 맞춰 주었는데 정작 데이터가 String으로 와서 list에 뿌려주지를 못하고 있습니다. Postman으로 확인했을 때는 정상적으로 JSON 데이터를 반환받는데 리액트에서 확인할려면 res.data가 "{\"dtoList\":[{\"tno\":115,\"title\":\"123zzzㅋㅋㅋㅋ\",\"content\":null,\"complete\":false,\"dueDate\":\"2025-08-07\",\"writer\":\"123\"} 위와 같이 스트링 형식으로 변환되어서 들어와서 오류가 생깁니다. 어디가 문제일까요..ㅠㅠ