묻고 답해요
141만명의 커뮤니티!! 함께 토론해봐요.
인프런 TOP Writers
-
미해결토비의 스프링 부트 - 이해와 원리
인텔리J에서 오픈한 프로젝트 스프링부트 설정관련 질문드립니다.
전 윈도우 기반에서 선생님 온라인 강의 청취하고 있습니다. git besh에서 sdkman으로 springboot 설치해서 터미널 상에서 spring 서버로 테스트까진 확인하고 터미널에서 인텔리J로 접속이 되지않아 그냥 인텔리J에서 프로젝트 오픈하여 수업을 따라가고 있었는데 인텔리J와 스프링부트 연동하는 방법을 몰라서 질문 남깁니다.
-
미해결토비의 스프링 부트 - 이해와 원리
강의 수강 상황 초기화 부탁드립니다.
강의를 제가 빠르게 넘겨가면서 훑다가 수료증이 나와버렸는데요.이 강의를 처음부터 제대로 수강하고 수료증을 새로 받고 싶습니다.부탁드리겠습니다~
-
미해결토비의 스프링 부트 - 이해와 원리
HelloRepositoryTest의 오류...
안녕하세요 강사님!강의 거의 막바지를 달려가고있네요 좋은강의 감사합니다! 다른게아니고 HelloRepositoryTest를 돌리는데 expected: null but was: tobyspring.helloboot.Hello@5546e754org.opentest4j.AssertionFailedError: expected: null but was: tobyspring.helloboot.Hello@5546e754 at java.base@11/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) at java.base@11/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62) at java.base@11/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) at app//tobyspring.helloboot.HelloRepositoryTest.findHelloFailed(HelloRepositoryTest.java:17) at java.base@11/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at java.base@11/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at java.base@11/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.base@11/java.lang.reflect.Method.invoke(Method.java:566) at app//org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:725) at app//org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60) at app//org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:131) at app//org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:149) at app//org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestableMethod(TimeoutExtension.java:140) at app//org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestMethod(TimeoutExtension.java:84) at app//org.junit.jupiter.engine.execution.ExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(ExecutableInvoker.java:115) at app//org.junit.jupiter.engine.execution.ExecutableInvoker.lambda$invoke$0(ExecutableInvoker.java:105) at app//org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106) at app//org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:64) at app//org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:45) at app//org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:37) at app//org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:104) at app//org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:98) at app//org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$7(TestMethodTestDescriptor.java:214) at app//org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) at app//org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:210) at app//org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:135) at app//org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:66) at app//org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:151) at app//org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) at app//org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141) at app//org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137) at app//org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139) at app//org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) at app//org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138) at app//org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95) at java.base@11/java.util.ArrayList.forEach(ArrayList.java:1540) at app//org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41) at app//org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155) at app//org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) at app//org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141) at app//org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137) at app//org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139) at app//org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) at app//org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138) at app//org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95) at java.base@11/java.util.ArrayList.forEach(ArrayList.java:1540) at app//org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41) at app//org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155) at app//org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) at app//org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141) at app//org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137) at app//org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139) at app//org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) at app//org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138) at app//org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95) at app//org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:35) at app//org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57) at app//org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:54) at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:107) at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:88) at org.junit.platform.launcher.core.EngineExecutionOrchestrator.lambda$execute$0(EngineExecutionOrchestrator.java:54) at org.junit.platform.launcher.core.EngineExecutionOrchestrator.withInterceptedStreams(EngineExecutionOrchestrator.java:67) at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:52) at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:114) at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:86) at org.junit.platform.launcher.core.DefaultLauncherSession$DelegatingLauncher.execute(DefaultLauncherSession.java:86) at org.junit.platform.launcher.core.SessionPerRequestLauncher.execute(SessionPerRequestLauncher.java:53) at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor$CollectAllTestClassesExecutor.processAllTestClasses(JUnitPlatformTestClassProcessor.java:99) at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor$CollectAllTestClassesExecutor.access$000(JUnitPlatformTestClassProcessor.java:79) at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor.stop(JUnitPlatformTestClassProcessor.java:75) at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.stop(SuiteTestClassProcessor.java:62) at java.base@11/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at java.base@11/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at java.base@11/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.base@11/java.lang.reflect.Method.invoke(Method.java:566) at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:36) at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24) at org.gradle.internal.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:33) at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:94) at com.sun.proxy.$Proxy2.stop(Unknown Source) at org.gradle.api.internal.tasks.testing.worker.TestWorker$3.run(TestWorker.java:193) at org.gradle.api.internal.tasks.testing.worker.TestWorker.executeAndMaintainThreadName(TestWorker.java:129) at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:100) at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:60) at org.gradle.process.internal.worker.child.ActionExecutionWorker.execute(ActionExecutionWorker.java:56) at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:113) at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:65) at app//worker.org.gradle.process.internal.worker.GradleWorkerMain.run(GradleWorkerMain.java:69) at app//worker.org.gradle.process.internal.worker.GradleWorkerMain.main(GradleWorkerMain.java:74) 이런에러가나서요 package tobyspring.helloboot;import org.junit.jupiter.api.Test;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.test.context.SpringBootTest;import org.springframework.jdbc.core.JdbcTemplate;import static org.assertj.core.api.Assertions.assertThat;@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE)public class HelloRepositoryTest {@Autowired JdbcTemplate jdbcTemplate; @Autowired HelloRepository helloRepository; @Test void findHelloFailed(){assertThat(helloRepository.findHello("Toby")).isNull(); }@Test void increaseCount(){assertThat(helloRepository.countOf("Toby")).isEqualTo(0); helloRepository.increaseCount("Toby"); assertThat(helloRepository.countOf("Toby")).isEqualTo(1); helloRepository.increaseCount("Toby"); assertThat(helloRepository.countOf("Toby")).isEqualTo(2); }}이건 제 코드입니다 어디서 오류가났는지를 모르겠습니다...
-
해결됨토비의 스프링 부트 - 이해와 원리
여러 DataSource 빈 등록 우선순위를 가늠할 수 없었습니다
안녕하세요 토비님. 우선, 감동으로 청강을 이어갈 기회를 얻게되어 참 감사드립니다.아래는 섹션8 - DataSource 자동 구성 클래스 파트의 @Bean 메서드 등록과정에 생긴 문제입니다.의문점은 메서드 선언 순서에 영향을 받는다는것을 알게되서 다음처럼 2번의 테스트를 진행했습니다.@Bean 메서드 선언순서 에 따른 등록 테스트simple , hikarihikari, simple 1 결과: simple 이 등록됨 /* DataSourceConfig.java */ @Bean @ConditionalOnMissingBean public DataSource simpleDriverDataSource() { ... } @Bean @ConditionalOnMissingBean @ConditionalMyOnClass("com.zaxxer.hikari.HikariDataSource") public DataSource hikariDataSource() { ... }hikari 에 `@ConditionalMyOnClass` 가 있기때문에 등록될 것으로 예상했지만, simple 이 등록되었습니다. 2 결과: hikari 가 등록됨 @Bean @... public DataSource hikariDataSource() { ... } @Bean @... public DataSource simpleDriverDataSource() { ... } 기묘하게도 @Bean 메서드 선언순서를 바꾸니 Hikari 빈이 등록되었습니다. 이상하다 싶어, @Primary 와 @Order(n) 을 주고 다시 테스트 해봤지만, 영향을 주지 않았습니다. 질문: 위와 같이 @Bean 메서드 순서에 영향을 받는 이유가 궁금합니다.이 결과로 @ConditionalOnClass 의 인식되는 FQCN 의 조건을 바꿔보기도 하고, 위치도 바꿔봤는데, 이렇다할 동작방식을 파악하기 힘들었습니다.그리고 @Primay 나 @Order 를 붙여가며 추가적인 등록순서에 대해 테스트를 진행했으나, 이것들 역시 영향을 주지 않았습니다.여러 곳에서 알아본 바, @Bean 메서드는 등록순서에 영향을 주지 않는것으로 알고있었는데, 이런 경우에는 어떤 매커니즘이 동작하는지가 궁금합니다. 이 결과를 보고 생각에 혼란이 와서 질문을 드리게 되었어요. 혹시 제가 놓친것이 있다면 조언을 부탁드려도 되겠습니까? 읽어주셔서 감사합니다☺️
-
미해결토비의 스프링 부트 - 이해와 원리
DataSource 자동 구성 클래스 10:07 테스트에서 에러발생
안녕하세요 강사님 강의 듣던 도중에 아래와 같은 에러가 발생해서요 서버를 돌렸을때는 에러없이 정상적으로 돌아갑니다. 구글링해서 이것저것 해보았는데 원인을 못찾아서 이렇게 질문 올립니다!
-
해결됨토비의 스프링 부트 - 이해와 원리
안녕하세요 강사님 JettyWebServerFactory Bean생성 하는데서 오류가납니다
org.springframework.context.ApplicationContextException: Unable to start web server; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'jettyWebServerFactory' defined in class path resource [tobyspring/config/autoconfig/JettyWebServerConfig.class]: Post-processing of merged bean definition failed; nested exception is java.lang.IllegalStateException: Failed to introspect Class [org.springframework.boot.web.embedded.jetty.JettyServletWebServerFactory] from ClassLoader [jdk.internal.loader.ClassLoaders$AppClassLoader@2437c6dc] 이런에러가나구요 package tobyspring.config.autoconfig;import org.springframework.boot.web.embedded.jetty.JettyServletWebServerFactory;import org.springframework.boot.web.servlet.server.ServletWebServerFactory;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Condition;import org.springframework.context.annotation.ConditionContext;import org.springframework.context.annotation.Conditional;import org.springframework.core.type.AnnotatedTypeMetadata;import tobyspring.config.MyAutoConfiguration;@MyAutoConfiguration@Conditional(JettyWebServerConfig.JettyCondition.class)public class JettyWebServerConfig {@Bean("jettyWebServerFactory")public ServletWebServerFactory servletWebServerFactory(){return new JettyServletWebServerFactory(); }static class JettyCondition implements Condition {@Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {return true; }}} 이건 코드입니다 왜 오류가날까요 틀린게없는거같습니다
-
해결됨토비의 스프링 부트 - 이해와 원리
처음에 sdkman 설치하는데서 설치가안됩니다(윈도우)
안녕하세요 강사님 제가 다른게아니고 윈도우에서 sdkman 설치를하려고하는데요계속 이런오류가나오면서 source자체가 먹히지가않습니다powershell에서 입력했구요 어떻게해야할까요 ㅠ
-
미해결토비의 스프링 부트 - 이해와 원리
imports 파일 생성
tobyspring.config.MyAutoConfiguration.imports 파일 생성 후 포맷이 변경되지 않는 것 같습니다.그냥 파일이 된 것 같습니다. imports 포맷의 파일로 생성이 되지 않는 것 같아서 따로 다운받거나 설정해야 할 것이 있을까요?
-
해결됨토비의 스프링 부트 - 이해와 원리
@Import에서 @Component가 없어도 동작합니다.
강사님께서@Import 어노테이션은@Component 어노테이션이 붙은 혹은메타 어노테이션으로 @Component가 붙은 클래스를 컴포넌트 스캔 대상이 아닌 클래스를 구성정보로 등록할수 있다고 말씀해주셨습니다. 호기심에 Import할 클래스 레벨에 어노테이션을 붙이지 않고public class ServerConfigTest { @Bean Name1 name1(){ return new Name1(name3()); } @Bean Name2 name2(){ return new Name2(name3()); } @Bean Name3 name3(){ return new Name3(); } static class Name1{ Name3 name3; public Name1(Name3 name3) { System.out.println("네임 1 등장이요"); } } static class Name2{ public Name2(Name3 name3) { System.out.println("네임 2 등장이요"); } } static class Name3{ public Name3() { System.out.println("네임 3 등장이요"); } } }@MyAutoConfiguration @Import(ServerConfigTest.class) public class ServerTestBean2 { //코드.. }결과는 @Component,@Configuration(proxyBeanMethods=false)와 같은 결과가 로그로 보이더라구요 래퍼런스에는 일반 구성 클래스 as well as regular component classes(AnnotationConfigApplicationContext.register와 유사) 라고 적혀있는데 그 의미가import가applicationContext.register(ServerConfigTest) 같은 방식으로 동작한다고 생각하면 될까요??
-
미해결토비의 스프링 부트 - 이해와 원리
Service 계층에서 테스트 관련해서 질문이 있습니다.
안녕하세요 토비님제가 프로젝트를 진행하면서 도저히 모르는 부분이 있습니다.강의와 관련이 없는 질문이지만, 간절한 마음으로 질문해봅니다 Service 계층은 상태검증과 행위 검증에 대한 고민이 있습니다.특히, 객체의 책임과 테스트 범위에 대한 관점에 대한 차이때문에 고민이 있습니다.우선, 코드를 보여드리겠습니다.CommunityCommandService.updateCommunity는 커뮤니티의 소개란과 해시태그를 업데이트하는 부분입니다.//CommunityCommandService.java public void updateCommunity(Long userId, Long communityId, String description, List<String> newTags) { Community community = communityRepository.findCommunityById(communityId); memberQueryService.getManager(userId, communityId); community.updateCommunity(description, newTags); }해당 코드는 communityId로 community를 가져오고, userId / communityId로 요청한 유저가 메니저인지 확인합니다.그 후, community 객체에게 update를 위임합니다. 그러면 community 객체는 내부 상태값을 변경합니다. 여기서, 상태 검증인지 행위 검증인지에 따라 테스트가 달라집니다.public class CommunityCommandServiceTest { @Test void 상태검증_테스트() { Community community = new Community("dummy Intro", List.of("dummy tag")); given(communityRepository.findById(any)).willReturn(community); communityCommandService.updateCommunity(1L, "new intro", List.of("new tag")); assertThat(community.getIntroduce).isEqualTo("new intro"); assertThat(community.getTags).containsExactly("new tag"); } @Test void 행위검증_테스트() { Community community = mock(Community.class); given(communityRepository.findById(any)).willReturn(community); communityCommandService.updateCommunity(1L, "new intro", List.of("new tag")); then(community).should(times(1)).update("new intro", List.of("new tag")) } }상태검증_테스트의 검증 부분을 보면, 위임한 결과에 대해서 테스트를 진행하고 있습니다. Community 클래스의 update를 또 테스트하는 것 같은 느낌이 있습니다. 즉, 서비스 계층의 테스트 영역을 넘어서는 것인지 의문입니다.반면, 행위검증_테스트는 community.update가 호출하면서 위임했는지에 대해서만 테스트합니다. 하지만, 내부 로직에 하드코딩 되어있는 듯 합니다.사실 저는 상태검증을 더 선호합니다. 하지만 상태 검증이 객체지향스러운지 잘 모르겠습니다. 어느정도 감수해야하는 것 일까요? 정리하자면,상태검증CommunityCommandService.updateCommunity로 변경된 상태를 테스트개인적으로 선호하는 방식. 하지만, 상태를 테스트하기 때문에 객체지향의 관점에서 맞는지 확신이 없다. 테스트코드는 이 부분을 감수하는 것인지?행위검증협력한 객체의 행위에 대한 테스트객체지향의 관점에서 위임이 잘 이루어졌는지 테스트하는게 자연스럽다고 생각 질문하자면,서비스계층에서 Community.update()로 커뮤니티 내부 값에 대한 변경을 요청하였습니다.객체지향에서는 객체들이 서로 책임을 위임하며 상호작용하는 것이기 때문에Service 계층에서는 위임이 되었는지 호출 여부만 판단하는게 적절한지, 아니면, Service 계층에서 위임한 그 결과 Community의 내부값을 바꾼게 적절한 테스트인지..만약 이 방법이 맞다면 객체지향스럽다고 말할 수 있는지..?어떤 방식이 적절한지 잘 모르겠습니다. 이론적인 부분과 실제 테스트에 대한 괴리때문에 발생하는 문제 같습니다. 긴 질문 읽어주셔서 감사드립니다!
-
해결됨토비의 스프링 부트 - 이해와 원리
강사님 bind, bindOrCreate 차이 질문있습니다.
Binder.get(evn).bind("",bean.getClass());Binder.get(evn).bindOrCreate("",bean.getClass()); 해당 클래스 타입으로 Environment 빈 정보를 읽어서값을 바인딩해주고 null이 될경우 새 빈 오브젝트로 만들어준다고 하셨는데 1.여기서 바인딩 결과가 null이 될경우라는게 무슨 의 미인지 잘 이해가 안갑니다. 그리고 저희가 만든 @MyConfigurationProperties 어노테이션에는@Component가 메타어노테이션이 들어있고실제 스프링이 사용하는@ConfigurationProperties 에는@Component가 없더라구요@EnableConfigurationProperties에서는직접 레지스트리에 저장하는거같은데 제가 이해한게 맞을까요?
-
해결됨토비의 스프링 부트 - 이해와 원리
@Value에 대해서 질문있습니다.
@MyAutoConfiguration class SampleBeanConfigClass { @Value("${name}") String name; @Bean SampleBean sampleBean(){ System.out.println("sampleBean name = "+name); } }@Configuration class UserConfigClass { @Value("${name}") String name; public UserConfigClass(){ System.out.println("UserConfigClass name = "+name); } @Bean public UserConfigBean userConfigBean(){ System.out.println("UserConfigBean name = "+name); return new UserConfigBean(); } }//application.properties name = springsampleBean name = ${name} UserConfigClass name = null UserConfigBean name = spring아직 PropertySourcesPlaceholderConfigurer는 MyAutoConfiguration으로 등록하지 않은 상태입니다. 그런데 @Value의 위치에 따라null, spring, ${name} 이렇게 각각 나오더라구요이렇게 value의 위치마다 다르게 작동하는 이유가 있을까요..?
-
해결됨토비의 스프링 부트 - 이해와 원리
@ComponentScan과 @Import 질문있습니다.
@ComponentScan을 하게되면해당 패키지 이하로 컴포넌트가 어노테이션으로 있으면빈으로 등록해주고@Import를 하면 @Configuration class를 넣으면해당 구정 정보 클래스가 빈 등록이 되는데 제가 생각한 @ComponentScan과 @Import차이는컴포넌트 스캔은 패키지나 클래스를 기점으로 쭉 클래스를 찾아 돌아다녀야하지만자동 구성빈에서 사용되는 많은 빈들을 다 컴포넌트 스캔으로 사용하는거보다필요한 구성 정보 클래스만 스프링 부트가 @Conditional로 판단해서 빈으로 등록할수 있다보니컴포넌트 스캔보다 더 빠르고 구성 정보끼리 관계를 알수 있다고 생각이 되더라구요 제가 이해한게 맞는지 아니면 잘못 이해하고 있는지 추가로 설명해주실수 있으신가요..?
-
미해결토비의 스프링 부트 - 이해와 원리
안녕하세요.
안녕하세요. 토비님덕분에 좋은 강의를 잘 보고 있습니다!강의와는 다른 내용인데.토비님은 REST API를 개발하실때다양한 쿼리스트링 요청이 들어올때 값의 유무 체크를 어떻게 하시는지 궁금합니다.if문으로 null을 각각 확인하고 있긴 한데..코드도 길어지고 가독성도 안좋아 질문드립니다 ㅠㅠpublic class User { private String name; private String phone; private String email; } @GetMapping("/v1") public void list(@ModelAttribute User user) { } :8080/v1?name=apple :8080/v1?name=apple&phone=01012345678 :8080/v1?phone=01012345678
-
해결됨토비의 스프링 부트 - 이해와 원리
@ConditionalOnMissingBean
선생님 안녕하세요 좋은 강의 제작해주셔서 정말 감사합니다. @ConditionalOnMissingBean 관련 궁금한 사항이 있습니다.수업에서는 아래처럼 ServletWebServerFactory 빈을 직접 만들었기에, @ConditionalOnMissingBean 주석 역시 직접 붙이셨습니다.@MyAutoConfiguration @ConditionalMyOnClass("org.apache.catalina.startup.Tomcat") public class TomcatWebServerConfig { @Bean("tomcatWebServerFactory") @ConditionalOnMissingBean public ServletWebServerFactory servletWebServerFactory() { return new TomcatServletWebServerFactory(); } } 그럼 위처럼 직접 생성한 auto config 파일이 아니라, 스프링부트 자체의 auto config 클래스에도 자체적으로 @ConditionalOnMissingBean 주석이 붙어있다고 생각하면 될까요?가령 스프링부트 프로젝트에서 자체 Tomcat 컨테이너를 만들면, 기존 springboot-starter의 톰캣과 충돌이 일어날 거라 생각되는데, @ConditionalOnMissingBean 이 미리 starter하위에 설정돼 있어서 충돌을 회피할 수 있는 것인지 궁금합니다.!
-
해결됨토비의 스프링 부트 - 이해와 원리
@TestPropertySource 사용에 대해서 궁금하게 있습니다.
강사님께서 application.properties는 스프링 프레임워크의 기본 동작 방식이 아니다스프링 부트 초기화 과정에서 추가 해주는 거라고 말씀해주셨는습니다. @ContextConfiguration는 통합 테스트를 위해서 ApplicationContext를 로드하고, 구성요소 클래스를 넘겨주면 빈을 등록해주는거 같더라구요 SpringApplication.run을 할경우에는Environment 구현체는 ApplicationServletEnvironment가 빈으로 등록이 되고,@ContextConfiguration로 테스트를 할경우에는Environment 구현체는 StandardEnvironment 구현체가 빈으로 등록되는걸 확인했습니다.그래서 Enviroment 구현체가 등록이 되면application.properties를 가져올수있지 않나? 라는 생각에 해당 구현체를 찾아보니StandardEnvironment 는 propertySource를 받지 않는걸 확인했습니다. 제가 궁금한건1.같은 구성 정보 클래스를 넘겨주고 빈으로 등록을 했는데 구현체가 달라지는지 궁금합니다.2.스프링부트에서 테스트를 하는데@TestPropertySource를 따로 명시적으로 메타어노테이션으로 선언해서 사용하는 이유가 궁금합니다.
-
해결됨토비의 스프링 부트 - 이해와 원리
@MyAutoConfiguration 에서 궁금한게 있습니다.
강사님께서 JdbcTemplate 과 JdbcTransactionManager는따로 클래스를 만들고 적용하는 순서를 지정해야하는게 스프링 부트에는 있다고 하셨고정석은 각각 다른 클래스로 만들고 적용하는 순서를 지정하는게 필요하다이렇게 말씀해주셨는데 1.정석 방법을 찾아보고싶은데 어떻게 검색을 해야할까요?그리고 편의 상 하나의 구성 정보에 넣으셨는데2.하나의 구성정보하는게 더 간편한데 스프링은 정석인 방법 클래스를 나누어서 순서를 정해놓은 이유가 궁금합니다.
-
해결됨토비의 스프링 부트 - 이해와 원리
메타어노테이션을 파악하는 주체가 디스패처 서블릿 -> 에 대한 질문이 있습니다!
안녕하세요 토비님!! @Component 스캔 강의 마지막 즈음에 1. @RestController는 내부에 @ResponseBody를 메타어노테이션으로 가지고 있기에 디스패처 서블릿이 컨트롤러에 @ResponseBody가 붙어있는 것으로 인지할 수 있고 2. 더 나아가 @RestController와 @Controller가 클래스 레벨에 붙게 되면 @RequestMapping을 삭제할 수 있기 때문에 디스패처 서블릿이 내부적으로 매핑 정보가 있음을 인식한다고 말씀하셨습니다!! 이전에 자바 웹 프로그래밍 Next Step 책으로 공부를 했을때 디스패처 서블릿은 내부적으로 핸들러에 대한 매핑을 가지고 있고 핸들러 매핑 구현체는 ApplicationContext를 통해 @Controller 어노테이션이 붙어있는 클래스를 요청하여 받아와 사용하기 편리한 형태의 오브젝트로 만들어 보관하는것으로 알고 있습니다! 만약 책에서 공부했던 내용과 실제 Spring MVC 구조가 동일하다면, 위와 같은 상황 때문에 디스패처 서블릿이 내부적으로 매핑 정보가 있음을 인식한다고 말씀하신것인지 확인차 질문드립니다!! 그리고 1번의 경우를 실제 코드레벨로 보고 싶어서 찾아보고 있는데 어떤 코드를 봐야할지 모르겠습니다.. 혹시 약간의 힌트를 주실 수 있을까요?
-
해결됨토비의 스프링 부트 - 이해와 원리
강사님 테스트 코드에 사용하신 클래스들은 어디서 알게되신건가요..?
스프링 부트 공부하다가 왜 이렇게 되는지 찾아봐도제대로 설명해주는 곳이 없어서 아쉬웟는데강의를 들으면서 정말 만족하면서 듣고 있습니다. 테스트 코드에 사용하는 클래스나, 매서드들은 배우는 입장에서는 강사님께서 알고계신 정보를 주입해주는 방법말고는 스프링 지식이 부족해서 알기가 어렵더라구요 강사님이 강의 진행을 하시면서 사용하시는테스트 코드나 매서드들의 정보는 스프링 doc나 어디를 가야 공식문서처럼 볼수있는 곳이 있을까요?스스로 필요한 테스트 클래스나 매서드들 찾아서 공부해보고 싶어서 문의를 남겨봅니다,
-
미해결토비의 스프링 부트 - 이해와 원리
강의 초반에 나오는 코드에서 에러가 납니다.
0:05초 부분에서 applicationContext를 익명클래스가 아닌 람다 표현식으로 바뀌어져있는데, 이 부분을 그대로 따라 쳤는데, 에러가 납니다. public static void main(String[] args) { AnnotationConfigWebApplicationContext applicationContext = onRefresh() -> { super.onRefresh(); ServletWebServerFactory serverFactory = this.getBean(ServletWebServerFactory.class); DispatcherServlet dispatcherServlet = this.getBean(DispatcherServlet.class); WebServer webServer = serverFactory.getWebServer(servletContext -> { servletContext.addServlet("dispatcherServlet", dispatcherServlet) .addMapping("/*"); }); webServer.start(); }; applicationContext.register(HellobootApplication.class); // 구성정보가 있는 클래스 등록 applicationContext.refresh(); } } onRefresh() -> {... }이 부분이 이해가 잘 되지 않는데, 어느 부분이 잘못된 건가요?AnnotationConfigWebApplicationContext applicationContext = onRefresh() -> { super.onRefresh();