작성
·
325
0
@SpringBootTest
@Transactional
class MemberServiceIntegrationTest {
@Autowired MemberService memberService;
@Autowired MemberRepository memberRepository;
@Test
@Commit
void 회원가입() {
Member member = new Member();
member.setName("habsell1st400");
Long saveId = memberService.join(member);
Member findMember = memberService.findOne(saveId).get();
assertThat(member.getName()).isEqualTo(findMember.getName());
// 이미 존재하는 이름인지 확인 (중복 확인)
}
@Test
public void 중복_회원_예외(){
// given
Member member1 = new Member();
member1.setName("spring3vc2");
Member member2 = new Member();
member2.setName("spring3vc2");
memberService.join(member1);
IllegalStateException e = assertThrows(IllegalStateException.class, () -> memberService.join(member2));
assertThat(e.getMessage()).isEqualTo("이미 존재하는 회원입니다.");
}
}
답변 4
0
제가 첫 프로젝트 settings 할 때 저장 경로가 잘못 지정하여
hello-springs에 .gradle 디렉터리 및 파일들이 다 저장된 것 같습니다.
결론
선생님, 완성된 프로젝트 참고해서 수정해보겠습니다!
선생님 마지막 질문으로, 현재 스프링 입문 수업을 들으며 배웠던 스프링 DB 접근 및 AOP를 새로운 프로젝트에다가 구현하기는 힘들거 같습니다. (소스를 보면 '수업시간에 배운 게 이거구나' 정도는 가능할 것 같습니다.)
혹시 이후 강의에서(스프링 핵심 원리 - 기본편, 스프링 MVC 1편 등) 다시한번 깊게 스프링 DB 및 AOP를 배우게 되는걸까요??
0
OMG 선생님, 안녕하세요.
알려주신 내용 적어놓겠습니다ㅎㅎ
제가 이해 못한 부분은,
member1에 spring123e2 입력
member2에 pring123vc2 입력
이와 같이 다를경우
org.opentest4j.AssertionFailedError: Expected java.lang.IllegalStateException to be thrown, but nothing was thrown.
at org.junit.jupiter.api.AssertThrows.assertThrows(AssertThrows.java:71)
at org.junit.jupiter.api.AssertThrows.assertThrows(AssertThrows.java:37)
at org.junit.jupiter.api.Assertions.assertThrows(Assertions.java:3007)
at hello.hellospring.service.MemberServiceIntegrationTest.중복_회원_예외(MemberServiceIntegrationTest.java:54)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:688)
at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:131)
at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:149)
at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestableMethod(TimeoutExtension.java:140)
at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestMethod(TimeoutExtension.java:84)
at org.junit.jupiter.engine.execution.ExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(ExecutableInvoker.java:115)
at org.junit.jupiter.engine.execution.ExecutableInvoker.lambda$invoke$0(ExecutableInvoker.java:105)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:64)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:45)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:37)
at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:104)
at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:98)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$6(TestMethodTestDescriptor.java:210)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:206)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:131)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:65)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:139)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:129)
at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:127)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:126)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:84)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1541)
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:143)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:129)
at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:127)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:126)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:84)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1541)
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:143)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:129)
at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:127)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:126)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:84)
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:32)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:51)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:108)
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:96)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:75)
at com.intellij.junit5.JUnit5IdeaTestRunner.startRunnerWithArgs(JUnit5IdeaTestRunner.java:71)
at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:235)
at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:54)
2021-10-24 11:59:09.346 INFO 13984 --- [extShutdownHook] j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default'
2021-10-24 11:59:09.361 INFO 13984 --- [extShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown initiated...
2021-10-24 11:59:09.361 INFO 13984 --- [extShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown completed.
Process finished with exit code -1
오류가 발생하고,
member1 : spring10
member2 : spring10
일 경우 정상적으로 실행이 됩니다.
정상적으로 실행 후 중복_회원_예외() 실행 결과 내용
2021-10-24 12:04:15.747 INFO 4496 --- [ main] o.s.t.c.transaction.TransactionContext : Began transaction (1) for test context [DefaultTestContext@65fe9e33 testClass = MemberServiceIntegrationTest, testInstance = hello.hellospring.service.MemberServiceIntegrationTest@45c28c49, testMethod = 중복_회원_예외@MemberServiceIntegrationTest, testException = [null], mergedContextConfiguration = [WebMergedContextConfiguration@18bc345 testClass = MemberServiceIntegrationTest, locations = '{}', classes = '{class hello.hellospring.HelloSpringApplication}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{org.springframework.boot.test.context.SpringBootTestContextBootstrapper=true}', contextCustomizers = set[org.springframework.boot.test.autoconfigure.actuate.metrics.MetricsExportContextCustomizerFactory$DisableMetricExportContextCustomizer@626abbd0, org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizer@0, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizerFactory$Customizer@1c9b0314, org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizer@6e2aa843, org.springframework.boot.test.json.DuplicateJsonObjectContextCustomizerFactory$DuplicateJsonObjectContextCustomizer@1224144a, org.springframework.boot.test.mock.mockito.MockitoContextCustomizer@0, org.springframework.boot.test.web.client.TestRestTemplateContextCustomizer@2235eaab, org.springframework.boot.test.context.SpringBootTestArgs@1, org.springframework.boot.test.context.SpringBootTestWebEnvironment@50de0926], resourceBasePath = 'src/main/webapp', contextLoader = 'org.springframework.boot.test.context.SpringBootContextLoader', parent = [null]], attributes = map['org.springframework.test.context.web.ServletTestExecutionListener.activateListener' -> true, 'org.springframework.test.context.web.ServletTestExecutionListener.populatedRequestContextHolder' -> true, 'org.springframework.test.context.web.ServletTestExecutionListener.resetRequestContextHolder' -> true, 'org.springframework.test.context.event.ApplicationEventsTestExecutionListener.recordApplicationEvents' -> false]]; transaction manager [org.springframework.orm.jpa.JpaTransactionManager@8aa1562]; rollback [true] START: execution(Long hello.hellospring.service.MemberService.join(Member)) START: execution(Optional hello.hellospring.repository.SpringDataJpaMemberRepository.findByName(String)) Hibernate: select member0_.id as id1_0_, member0_.name as name2_0_ from member member0_ where member0_.name=? END: execution(Optional hello.hellospring.repository.SpringDataJpaMemberRepository.findByName(String)) 266ms START: execution(Member hello.hellospring.repository.MemberRepository.save(Member)) Hibernate: insert into member (id, name) values (null, ?) END: execution(Member hello.hellospring.repository.MemberRepository.save(Member)) 62ms END: execution(Long hello.hellospring.service.MemberService.join(Member)) 328ms START: execution(Long hello.hellospring.service.MemberService.join(Member)) START: execution(Optional hello.hellospring.repository.SpringDataJpaMemberRepository.findByName(String)) Hibernate: select member0_.id as id1_0_, member0_.name as name2_0_ from member member0_ where member0_.name=? END: execution(Optional hello.hellospring.repository.SpringDataJpaMemberRepository.findByName(String)) 16ms END: execution(Long hello.hellospring.service.MemberService.join(Member)) 16ms 2021-10-24 12:04:16.421 INFO 4496 --- [ main] o.s.t.c.transaction.TransactionContext : Rolled back transaction for test: [DefaultTestContext@65fe9e33 testClass = MemberServiceIntegrationTest, testInstance = hello.hellospring.service.MemberServiceIntegrationTest@45c28c49, testMethod = 중복_회원_예외@MemberServiceIntegrationTest, testException = [null], mergedContextConfiguration = [WebMergedContextConfiguration@18bc345 testClass = MemberServiceIntegrationTest, locations = '{}', classes = '{class hello.hellospring.HelloSpringApplication}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{org.springframework.boot.test.context.SpringBootTestContextBootstrapper=true}', contextCustomizers = set[org.springframework.boot.test.autoconfigure.actuate.metrics.MetricsExportContextCustomizerFactory$DisableMetricExportContextCustomizer@626abbd0, org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizer@0, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizerFactory$Customizer@1c9b0314, org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizer@6e2aa843, org.springframework.boot.test.json.DuplicateJsonObjectContextCustomizerFactory$DuplicateJsonObjectContextCustomizer@1224144a, org.springframework.boot.test.mock.mockito.MockitoContextCustomizer@0, org.springframework.boot.test.web.client.TestRestTemplateContextCustomizer@2235eaab, org.springframework.boot.test.context.SpringBootTestArgs@1, org.springframework.boot.test.context.SpringBootTestWebEnvironment@50de0926], resourceBasePath = 'src/main/webapp', contextLoader = 'org.springframework.boot.test.context.SpringBootContextLoader', parent = [null]], attributes = map['org.springframework.test.context.web.ServletTestExecutionListener.activateListener' -> true, 'org.springframework.test.context.web.ServletTestExecutionListener.populatedRequestContextHolder' -> true, 'org.springframework.test.context.web.ServletTestExecutionListener.resetRequestContextHolder' -> true, 'org.springframework.test.context.event.ApplicationEventsTestExecutionListener.recordApplicationEvents' -> false]]
중복_회원_예외
메서드 같은 경우, 중복인지 확인하기 위해 사용하는 메서드이며, member1, member2 입력 값이 다를 경우 예외가 발생합니다. (첫 번째 사진)
이렇게 진행되는게 맞는걸까요??
결론)
member1, member2 두 개의 값이 다를 경우 오류가 발생합니다.
member1, member2 두 개의 값이 같을 경우 실행이 잘되는 것 같습니다.(다만, 이미 존재하는 회원입니다. 라고 출력이 되지 않습니다.)
현재하는 테스트가 MemberServiceIntegrationTest 이고, 이전 강의의 MemberServiceTest에서는 정상작동하셨을거에요,
근데 MemberServiceIntegrationTest 에서, SpringDataJPA를 이용한 테스트에서 문제가 발생하는 것과,
본 강의에서 사용하지 않은 @Commit을 코드에서 사용하신 것으로 보아 다른 부분에서도 임의의 코드를 작성하였을 수도 있고, 필요한 코드를 누락하셨을 수도 있을것 같습니다.
발생하는 상황에 대해 설명해주신 것만으로는 제가 원인 파악이 잘 되지 않아 프로젝트 코드를 확인해 봐야 할 것 같습니다.
글 작성하실 때 "자주하는 질문"을 확인하여 프로젝트 코드를 업로드 후 링크를 공유해주시면 확인 후 답변드리겠습니다.
수업을 다 들은 후, 이전 과정 테스트하다 발생한 결과입니다.
섹션 6. 스프링 DB 접근 기술
섹션 7. AOP
AOP 수업을 들은 후, Test/hello.hellospring/service/MemberServiceIntegrationTest.java
에서 이전 섹션 6에서 배웠던 내용을 테스트하던 중, 발생한 상태입니다.
@Commit 애노테이션을 추가한 후 테스트를 수행하면 h2 DB에 데이터가 저장되는 것을 확인할 수 있다는 말씀에 추가하였습니다! (6. 스프링 DB 접근 기술, JPA에서)
용량이 : 5GB정도 (압축한 결과 1.3GB)
너무 커서 반디집으로 압축을 하였습니다.
선생님, 죄송합니다. 현재 어떠한 상태로 돌아가는 지 하루정도 찾아보았지만 원인을 모르겠습니다ㅠㅠ
0
안녕하세요. Chang96님, 공식 서포터즈 OMG입니다.
질문 내용은 "중복된 회원가입 검증" 관련 질문이라고 이해하였습니다.
중복회원예외 테스트는 다음과 같은 코드가 있습니다.
각각의 줄마다 설명드리겠습니다.
1, 3. member1,2 인스턴스를 생성합니다.
2, 4 . member1, 2의 이름을 설정합니다.
5. member1의 회원가입이 실행되는 코드입니다.
* 이제 여기서 회원가입이 어떠한 방식으로 진행되는지를 확인해야합니다.
join()은 총 3라인의 코드로 되어 있습니다.
1. 파라미터로 받은 member의 중복회원검증 로직입니다.
2. 검증로직을 통과하면 저장소에 저장합니다.(회원가입)
3. 해당 회원의 id를 반환합니다.
* 여기까지 봤을 때는 어떤 검증 절차가 있는지 확인이 불가능합니다. join()의 첫번째 라인의 메서드를 자세히 봐야합니다.
* 이제 어떤 검증로직이 있는지 확인이 가능합니다.
* validateDuplicateMember 는 파라미터로 받은 member의 이름을 저장소에서 찾습니다.
만약 존재하는 경우, 예외를 발생시킵니다.
* 메서드 안의 코드가 이해가 가지 않는다면 자바8의 Optional과 람다를 학습하셔야 합니다.
다시 테스트코드로 돌아와서 보면
6. member2를 회원가입 시킵니다. 현재 member1과 member2는 spring3vc2로 동일하므로 예외가 발생하며, validateDuplicateMember()에서 반환하였던 "이미 존재하는 회원입니다." 예외메시지를 포함한 IllegalStateException이 발생하며
7. 예외에서 발생한 메시지(e.getMessage())와 지정한 예외 메시지가 동일한지 isEqualTo(동일한지 검증) 합니다.
제 답변이 미흡하였거나 추가(부연) 설명이 필요한 경우 말씀해주세요.
감사합니다.
Q) 혹시 이후 강의에서(스프링 핵심 원리 - 기본편, 스프링 MVC 1편 등) 다시한번 깊게 스프링 DB 및 AOP를 배우게 되는걸까요??
A) 아래의 공지에서 확인하실 수 있으신데요, 곧 오픈 예정인 핵심원리 고급편에서 AOP에 대해 a부터z까지 학습 가능하십니다 ^^ 이후 강의인 "스프링 DB접근기술" 강의에서 스프링과 DB접근 관련한 강의도 빠른 시일내에 공개하실 예정이라고 하시니 이후에 공개되는 강의에서 학습하시는 것을 권장드립니다
====
안녕하세요. 여러분
드디어 스프링 핵심 원리 - 고급편 강의가 오픈 준비에 돌입했습니다.
오랜기간 기다려주시고 응원해주셔서 고맙습니다.
- 제목: 스프링 핵심 원리 - 고급편
- 출시 일정: 10월 27일(수) ~ 11월 3일(수) 사이에 출시
- 강의 수: 120강
- 강의 자료: PDF 360장
기본편 강의에서 스프링과 객체 지향 개발의 기본기를 학습했다면, 고급편 강의는 스프링과 백엔드 개발을 깊이있게 이해하기 위한 심도있는 주제들을 다룹니다.
고급편 핵심 주제
- 스프링 핵심 디자인 패턴
- 동시성 문제와 쓰레드 로컬
- 스프링 AOP
어려운 내용들이지만 실력있는 백엔드 개발자가 되려면 반드시 한번은 깊이있게 공부해야 하는 내용입니다.
복잡하고 어려운 스프링의 핵심 심화 내용을 이 강의 하나로 마무리 짓는다고 생각하시면 됩니다.
자세한 내용은 다음 강의 소개를 참고해주세요.
강의 소개
*스프링 프레임워크*
실력있는 개발자가 되려면 실무에서 자주 사용하는 도구를 깊이있게 이해해야 합니다.
스프링은 실무에서 가장 많이 사용하는 백엔드 기술입니다.
따라서 백엔드 개발자라면 스프링을 깊이있게 이해하는 것이 중요합니다.
우리는 앞서 스프링 핵심 원리 - 기본편에서 스프링과 객체 지향 개발의 기본기를 학습했습니다.
*스프링 핵심 원리 - 기본편*
객체 지향 설계와 스프링
- SOLID - SRP, OCP, DIP
스프링 컨테이너와 스프링 빈
- 스프링 컨테이너 - IoC, DI
- 의존관계 주입
- 빈 생성주기와 스코프
*스프링 핵심 원리 - 고급편*
스프링을 깊이있게 이해하고, 실무에서 실력있는 개발자가 되려면, 기본편에서 학습한 내용을 기반으로 크게 3가지 핵심 고급 개념을 알아야합니다. 이번 고급편에서는 이러한 고급 개념을 학습합니다.
1. 스프링 핵심 디자인 패턴
2. 동시성 문제를 다루기 위한 쓰레드 로컬
3. 스프링 AOP
*1. 스프링 핵심 디자인 패턴*
스프링의 내부 기술들은 몇 가지 핵심 디자인 패턴으로 만들어져 있습니다. 따라서 스프링을 깊이있게 이해하기 위해서는 먼저 스프링이 자주 사용하는 디자인 패턴들을 알아야합니다.
*강의에서 전달하는 핵심 디자인 패턴*
- 템플릿 메서드 패턴
- 전략 패턴
- 템플릿 콜백 패턴
- 프록시 패턴
- 데코레이터 패턴
강의에서는 이러한 디자인 패턴들을 예제 코드로 하나하나 만들어보면서 아주 쉽게 설명해드립니다. 그리고 이렇게 학습한 디자인 패턴을 애플리케이션에 적용하는 방법도 알아봅니다.
*2. 쓰레드 로컬*
스프링은 주로 웹 애플리케이션 개발에 사용됩니다. 웹 애플리케이션은 동시에 여러 요청을 처리하기 위해 멀티쓰레드를 사용합니다. 따라서 여러 쓰레드가 경합하는 동시성 문제가 발생할 수 있습니다.
*강의에서 전달하는 동시성 문제와 쓰레드 로컬 내용*
- 웹 애플리케이션
- 멀티쓰레드
- 동시성 문제
강의에서는 스프링을 사용할 때 발생하는 동시성 문제를 알아보고, 또 동시성 문제를 편리하게 해결할 수 있는 쓰레드 로컬 개념까지 학습합니다.
쓰레드 로컬은 멀티쓰레드 환경에서 스프링이 어떻게 동시성 문제 없이 잘 작동하는지 이해하기 위한 필수 개념입니다.
*3. 스프링 AOP*
스프링 AOP는 실무에서 정말 많이 사용되는, 마치 마법처럼 느껴질 정도로 편리한 기술입니다. 많은 개발자가 스프링 AOP를 사용하지만, 스프링 AOP가 어떻게 동작하는지 그 원리를 정확하게 이해하고 사용하는 개발자는 많지 않습니다.
단순히 스프링 AOP의 기능만 알고 사용하는 단계를 넘어서, 스프링 AOP의 동작 원리를 깊이있게 이해해야 장애가 발생했을 때 근본적인 문제 해결이 가능합니다.
*강의에서 전달하는 스프링 AOP 내용*
- 개념, 용어정리
- 프록시 - JDK 동적 프록시, CGLIB
- 동작 원리
- 실전 예제
- 실무 주의 사항
강의에서는 스프링 AOP의 단순한 기능 정리를 넘어서 동작 원리부터 실전 예제, 그리고 실무에서 자주 발생하는 주의 사항까지 스프링 AOP의 모든 것을 정리해드립니다.
*기타*
추가로 스프링 컨테이너의 고급 기술인 빈 후처리기와 스프링 애플리케이션을 개발하는 다양한 *실무 팁*도 전해드립니다.
- 스프링 컨테이너의 확장 포인트 - 빈 후처리기
- 스프링 애플리케이션을 개발하는 다양한 실무 팁
강의를 듣고 나면
고급편인 만큼 이 강의에서 다루는 내용들은 쉬운 내용들이 아닙니다.
하지만 실력있는 백엔드 개발자가 되려면 반드시 한번은 공부해야 하는 내용입니다.
포기하지 않고 이 강의를 끝까지 따라오시면 여러분은 개발자로서 엄청난 내공 상승을 경험하고 누구보다 자신있게 스프링을 사용하실 수 있게 됩니다.
어려운 내용이니 만큼 저도 철저하게 강의를 준비했습니다. 여러분이 이 내용들을 본인 것으로 완성할 수 있게 도와드리겠습니다.
감사합니다.