ํฐ์คํ ๋ฆฌ ๋ทฐ
๐ ์ฑ๊ธํค ํจํด
: ํด๋์ค์ ์ธ์คํด์ค๊ฐ ๋ฑ 1๊ฐ๋ง ์์ฑ๋๋ ๊ฒ์ ๋ณด์ฅํ๋ ๋์์ธ ํจํด
: ๊ฐ์ฒด ์ธ์คํด์ค๋ฅผ 2๊ฐ ์ด์ ์์ฑํ์ง ๋ชปํ๋๋ก ๋ง์์ผํจ (ex. ์์ฑ์๋ฅผ private์ผ๋ก ์ ์ธ)
: (-) ์ฑ๊ธํค ํจํด์ ๊ตฌํํ๋ ์ฝ๋ ์์ฒด๊ฐ ๋ง์ด ๋ค์ด๊ฐ๋ค
: (-) ์์กด๊ด๊ณ์ ํด๋ผ์ด์ธํธ๊ฐ ๊ตฌ์ฒด ํด๋์ค์ ์์กดํ๋ค โก๏ธ DIP ์๋ฐ, OCP ์๋ฐ ๊ฐ๋ฅ์ฑ ๅค
: (-) ๊ฒฐ๋ก ์ ์ผ๋ก ์ ์ฐ์ฑ ↓
๐ ์ฑ๊ธํค ์ปจํ ์ด๋
: ์คํ๋ง ์ปจํ ์ด๋๋ ์ฑ๊ธํค ํจํด์ ๋ฌธ์ ์ ์ ํด๊ฒฐํ๋ฉด์, ๊ฐ์ฒด ์ธ์คํด์ค๋ฅผ ์ฑ๊ธํค(1๊ฐ๋ง ์์ฑ)์ผ๋ก ๊ด๋ฆฌ
: ์ฑ๊ธํค ์ปจํ ์ด๋๋ ์ฑ๊ธํค ๋ ์ง์คํธ๋ฆฌ(์ฑ๊ธํค ๊ฐ์ฒด๋ฅผ ์์ฑํ๊ณ ๊ด๋ฆฌํ๋ ๊ธฐ๋ฅ)
: ์คํ๋ง ๋น์ด ์ฑ๊ธํค์ผ๋ก ๊ด๋ฆฌ๋๋ ๋น
- ๊ณ ๊ฐ์ ์์ฒญ์ด ์ฌ ๋ ๋ง๋ค ๊ฐ์ฒด๋ฅผ ์์ฑํ๋ ๊ฒ์ด ์๋๋ผ, ์ด๋ฏธ ๋ง๋ค์ด์ง ๊ฐ์ฒด๋ฅผ ๊ณต์ ํด์ ํจ์จ์ ์ผ๋ก ์ฌ์ฌ์ฉ!
โ ์ฑ๊ธํค ๋ฐฉ์์ ์ฃผ์์
: ๊ฐ์ฒด ์ธ์คํด์ค๋ฅผ ํ๋๋ง ์์ฑํด์ ๊ณต์ ํ๋ ์ฑ๊ธํค ๋ฐฉ์์ ์ฌ๋ฌ ํด๋ผ์ด์ธํธ๊ฐ ํ๋์ ๊ฐ์ ๊ฐ์ฒด ์ธ์คํด์ค๋ฅผ ๊ณต์ ํ๊ธฐ ๋๋ฌธ์ ์ฑ๊ธํค ๊ฐ์ฒด๋ ์ํ๋ฅผ ์ ์ง(stateful)ํ๊ฒ ์ค๊ณํ๋ฉด ์๋๋ค. ์ฆ, ๋ฌด์ํ(stateless)๋ก ์ค๊ณํด์ผ ํ๋ค!
- ํน์ ํด๋ผ์ด์ธํธ์ ์์กด์ ์ธ ํ๋๊ฐ ์์ผ๋ฉด ์๋๋ค.
- ํน์ ํด๋ผ์ด์ธํธ๊ฐ ๊ฐ์ ๋ณ๊ฒฝํ ์ ์๋ ํ๋๊ฐ ์์ผ๋ฉด ์๋๋ค.
- ๊ฐ๊ธ์ ์ฝ๊ธฐ๋ง ๊ฐ๋ฅํด์ผ ํ๋ค.
- ํ๋ ๋์ ์ ์๋ฐ์์ ๊ณต์ ๋์ง ์๋ ์ง์ญ๋ณ์, ํ๋ผ๋ฏธํฐ, ThreadLocal ๋ฑ์ ์ฌ์ฉํด์ผ ํ๋ค.
โ ์คํ๋ง ์ปจํ ์ด๋๊ฐ ์ฑ๊ธํค์ ๋ณด์ฅํด์ฃผ๋ ์ด์ - @Configuration๊ณผ ๋ฐ์ดํธ์ฝ๋ ์กฐ์
: ์คํ๋ง์ด CGLIB๋ผ๋ ๋ฐ์ดํธ์ฝ๋ ์กฐ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํด์ AppConfigํด๋์ค๋ฅผ ์์๋ฐ๋ ์์์ ๋ค๋ฅธ ํด๋์ค๋ฅผ ๋ง๋ค๊ณ , ๊ทธ ๋ค๋ฅธ ํด๋์ค๋ฅผ ์คํ๋ง ๋น์ผ๋ก ๋ฑ๋ก!
: ๊ทธ ์์์ ๋ค๋ฅธ ํด๋์ค๊ฐ ์ฑ๊ธํค์ด ๋ณด์ฅ๋๋๋ก ํด์ค!
- @Configuration์ ์ฌ์ฉํ์ง ์์ผ๋ฉด ์ฑ๊ธํค ๋ณด์ฅ X
๐ ์ปดํฌ๋ํธ ์ค์บ
: ์ค์ ์ ๋ณด๊ฐ ์์ด๋ ์๋์ผ๋ก ์คํ๋ง ๋น์ ๋ฑ๋กํ๋ ๊ธฐ๋ฅ
0๏ธโฃ AppConfig.java (์ค์ ํ์ผ) ๋ง๋ค๊ธฐ
@Configuration
@ComponentScan(
excludeFilters = @Filter(type = FilterType.ANNOTATION, classes =
Configuration.class)
)
public class AutoAppConfig {
}
- @ComponentScan ์ ๋ ธํ ์ด์ ๋ถ์ฌ์ฃผ๊ธฐ
- excludeFilters๋ฅผ ์ด์ฉํด ์ค์ ์ ๋ณด(@Configuration)๋ ์ปดํฌ๋ํธ ์ค์บ ๋์์์ ์ ์ธ
1๏ธโฃ ์คํ๋ง ๋น์ผ๋ก ๋ฑ๋กํ ํด๋์ค์ @Component ์ ๋ ธํ ์ด์ ๋ถ์ด๊ธฐ
@Component
public class MemoryMemberRepository implements MemberRepository {}
@Component
public class MemberServiceImpl implements MemberService {
private final MemberRepository memberRepository;
@Autowired
public MemberServiceImpl(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
}
- @Autowired๋ ์์กด๊ด๊ณ๋ฅผ ์๋์ผ๋ก ์ฃผ์
→ ๊ธฐ๋ณธ ์กฐํ ์ ๋ต์ ํ์ ์ด ๊ฐ์ ๋น์ ์ฐพ์์ ์ฃผ์ (getBean(MemberRepository.class)์ ๋์ผํ๋ค๊ณ ์ดํด)
- ์คํ๋ง ๋น์ ๊ธฐ๋ณธ ์ด๋ฆ์ ํด๋์ค๋ช ์ ์ฌ์ฉํ๋ ๋งจ ์๊ธ์๋ง ์๋ฌธ์๋ฅผ ์ฌ์ฉ (ex.memberServiceImpl)
*์คํ๋ง ๋น์ ์ด๋ฆ์ ์ง์ ์ง์ ํ๊ณ ์ถ์ผ๋ฉด
@Component("memberService2")
โ์ปดํฌ๋ํธ ์ค์บ ํ์ ์์น
@ComponentScan(
basePackages = "hello.core",
}
- basePackages: ํ์ํ ํจํค์ง์ ์์ ์์น๋ฅผ ์ง์ / basePackageClasses: ์ง์ ํ ํด๋์ค์ ํจํค์ง๋ฅผ ์์ ์์น๋ก ์ง์
- ๋ง์ฝ ์ง์ ํ์ง ์์ผ๋ฉด @ComponentScan์ด ๋ถ์ ์ค์ ์ ๋ณด ํด๋์ค์ ํจํค์ง๊ฐ ์์ ์์น!
๐ก ํจํค์ง ์์น๋ฅผ ์ง์ ํ์ง ์๊ณ , ์ค์ ์ ๋ณด ํด๋์ค์ ์์น๋ฅผ ํ๋ก์ ํธ ์ต์๋จ์ ๋๋ ๊ฒ์ ๊ถ์ฅ!
โ์ปดํฌ๋ํธ ์ค์บ ๊ธฐ๋ณธ ๋์
- @Component
- @Controller
- @Service
- @Repository : ์คํ๋ง ๋ฐ์ดํฐ ์ ๊ทผ ๊ณ์ธต์ผ๋ก ์ธ์, ๋ฐ์ดํฐ ๊ณ์ธต์ ์์ธ๋ฅผ ์คํ๋ง ์์ธ๋ก ๋ณํ
- @Configuration : ์คํ๋ง ์ค์ ์ ๋ณด๋ก ์ธ์, ์คํ๋ง ๋น์ด ์ฑ๊ธํค์ผ๋ก ์ ์งํ๋๋ก ์ถ๊ฐ ์ฒ๋ฆฌ
โ์ปดํฌ๋ํธ ์ค์บ์ ์ ์ธํ ๋๋ง์ ์ ๋ ธํ ์ด์ ์ ๋ง๋ค์ด ๋ณด์!
0๏ธโฃ ์ ๋ ธํ ์ด์ ๋ง๋ค๊ธฐ
// ์ปดํฌ๋ํธ ์ค์บ ๋์์์ ์ ์ธํ ์ ๋
ธํ
์ด์
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyExcludeComponent {}
1๏ธโฃ ์ํ๋ ํด๋์ค์ ์ ๋ ธํ ์ด์ ์ถ๊ฐ
@MyExcludeComponent
public class BeanB {}
2๏ธโฃ ์ค์ ํ์ผ์ ์ ์ฉ
@Configuration
@ComponentScan(
excludeFilters = @Filter(type = FilterType.ANNOTATION, classes =
MyExcludeComponent.class)
)
static class ComponentFilterAppConfig {}
- includeFilters : ์ปดํฌ๋ํธ ์ค์บ ๋์์ ์ถ๊ฐ๋ก ์ง์
- excludeFilters : ์ปดํฌ๋ํธ ์ค์บ์์ ์ ์ธํ ๋์์ ์ง์
โ๏ธ @Component๋ฉด ์ถฉ๋ถํ๊ธฐ ๋๋ฌธ์, includeFilters๋ฅผ ์ฌ์ฉํ ์ผ์ ๊ฑฐ์ ์๋ค. ๊ฐ์ธ์ ์ผ๋ก ์ต์ ์ ๋ณ๊ฒฝํ๋ฉด์ ์ฌ์ฉํ๊ธฐ ๋ณด๋ค๋ ์คํ๋ง์ ๊ธฐ๋ณธ ์ค์ ์ ์ต๋ํ ๋ง์ถ์ด ์ฌ์ฉํ๋ ๊ฒ์ ๊ถ์ฅ!
๐ ๋น ์ค๋ ๋ฑ๋ก๊ณผ ์ถฉ๋
1) ์๋ ๋น ๋ฑ๋ก vs ์๋ ๋น ๋ฑ๋ก
: ConflictingBeanDefinitionException ์์ธ ๋ฐ์
2) ์๋ ๋น ๋ฑ๋ก vs ์๋ ๋น ๋ฑ๋ก
: ์๋ ๋น ๋ฑ๋ก์ด ์ฐ์ (์๋ ๋น์ด ์๋ ๋น์ ์ค๋ฒ๋ผ์ด๋ฉ)
: ์ต๊ทผ ์คํ๋ง ๋ถํธ์์๋ ์๋ ๋น ๋ฑ๋ก๊ณผ ์๋ ๋น ๋ฑ๋ก์ด ์ถฉ๋๋๋ฉด ์ค๋ฅ๊ฐ ๋ฐ์!
๐ ์์กด๊ด๊ณ ์๋ ์ฃผ์
1๏ธโฃ ์์ฑ์ ์ฃผ์
- ์์ฑ์ ํธ์ถ์์ ์ ๋ฑ 1๋ฒ๋ง ํธ์ถ๋๋ ๊ฒ์ด ๋ณด์ฅ
- ๋ถ๋ณ, ํ์ ์์กด๊ด๊ณ์ ์ฌ์ฉ
@Component
public class OrderServiceImpl implements OrderService {
private final MemberRepository memberRepository;
private final DiscountPolicy discountPolicy;
@Autowired // ์์ฑ์๊ฐ ๋ฑ 1๊ฐ ์์ผ๋ฉด @Autowired ์๋ต ๊ฐ๋ฅ!
public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy discountPolicy) {
this.memberRepository = memberRepository;
this.discountPolicy = discountPolicy;
}
}
๐ก ๊ฒฐ๋ก ์ ์ผ๋ก, ์์ฑ์ ์ฃผ์ ์ ์ฌ์ฉํด์ผ ๋๋ค!
- ๋๋ถ๋ถ์ ์์กด๊ด๊ณ๋ฅผ ์ ํ๋ฆฌ์ผ์ด์ ์ข ๋ฃ ์ ๊น์ง ๋ณํ๋ฉด ์๋จ. (๋ถ๋ณ)
- ์์ ์ ์ฃผ์ ์ ์ฌ์ฉํ๋ฉด setXxx๋ฉ์๋๋ฅผ ํญ์ public์ผ๋ก ์ด์ด๋์ด์ผ ํ๋ค. (์ข์ ์ค๊ณ ๋ฐฉ๋ฒX)
- ์์ ์ ์ฃผ์ ์ ์ฌ์ฉํ๋ฉด ์์ํ ์๋ฐ ์ฝ๋๋ฅผ ๋จ์ ํ ์คํธํ๋ ๊ฒฝ์ฐ ์์กด ๊ด๊ณ๊ฐ ์์ผ๋ฉด ์คํ์ ๋์ง๋ง ๊ฒฐ๊ณผ๊ฐ ์ฌ๋ฐ๋ฅด์ง ์์. but ์์ฑ์ ์ฃผ์ ์ ์ฌ์ฉํ๋ฉด ์ฃผ์ ๋ฐ์ดํฐ๋ฅผ ๋๋ฝํ์ ๋ ์ปดํ์ผ ์ค๋ฅ๊ฐ ๋ฐ์!
2๏ธโฃ ์์ ์ ์ฃผ์ (setter ์ฃผ์ )
- ์ ํ, ๋ณ๊ฒฝ ๊ฐ๋ฅ์ฑ์ด ์๋ ์์กด๊ด๊ณ์ ์ฌ์ฉ
@Component
public class OrderServiceImpl implements OrderService {
private MemberRepository memberRepository;
private DiscountPolicy discountPolicy;
@Autowired
public void setMemberRepository(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
@Autowired
public void setDiscountPolicy(DiscountPolicy discountPolicy) {
this.discountPolicy = discountPolicy;
}
}
*@Autowired์ ๊ธฐ๋ณธ ๋์์ ์ฃผ์ ํ ๋์์ด ์์ผ๋ฉด ์ค๋ฅ๊ฐ ๋ฐ์. ์ฃผ์ ํ ๋์์ด ์์ด๋ ๋์ํ๊ฒ ํ๋ ค๋ฉด @Autowired(required = false)๋ก ์ง์ !
3๏ธโฃ ํ๋ ์ฃผ์
- ์ฝ๋๊ฐ ๊ฐ๊ฒฐํ์ง๋ง ์ธ๋ถ์์ ๋ณ๊ฒฝ์ด ๋ถ๊ฐ๋ฅํด์ ํ ์คํธํ๊ธฐ ํ๋ฌ (์ฌ์ฉํ์ง ๋ง์!)
@Component
public class OrderServiceImpl implements OrderService {
@Autowired
private MemberRepository memberRepository;
@Autowired
private DiscountPolicy discountPolicy;
}
4๏ธโฃ ์ผ๋ฐ ๋ฉ์๋ ์ฃผ์
- ํ๋ฒ์ ์ฌ๋ฌ ํ๋๋ฅผ ์ฃผ์ ๋ฐ์ ์ ์์
- ์ผ๋ฐ์ ์ผ๋ก ์ ์ฌ์ฉX
@Component
public class OrderServiceImpl implements OrderService {
private MemberRepository memberRepository;
private DiscountPolicy discountPolicy;
@Autowired
public void init(MemberRepository memberRepository, DiscountPolicy discountPolicy) {
this.memberRepository = memberRepository;
this.discountPolicy = discountPolicy;
}
}
โ ์์กด๊ด๊ณ ์๋ ์ฃผ์ ์ ์คํ๋ง ์ปจํ ์ด๋๊ฐ ๊ด๋ฆฌํ๋ ์คํ๋ง ๋น์ด์ด์ผ ๋์. Member๊ฐ์ ํด๋์ค์์ @Autowired ์ฝ๋๋ฅผ ์ ์ฉํด๋ ์๋ฌด ๊ธฐ๋ฅ๋ ๋์ X
โ ์๋ ์ฃผ์ ๋์์ ์ต์ ์ผ๋ก ์ฒ๋ฆฌํ๋ ๋ฐฉ๋ฒ
- @Autowired(required = false) : ์๋ ์ฃผ์ ํ ๋์์ด ์์ผ๋ฉด ์์ ์ ๋ฉ์๋ ์์ฒด๊ฐ ํธ์ถ X
- org.springframework.lang.@Nullable: ์๋ ์ฃผ์ ํ ๋์์ด ์์ผ๋ฉด null์ด ์ ๋ ฅ
- Optional<>: ์๋ ์ฃผ์ ํ ๋์์ด ์์ผ๋ฉด Optional.empty๊ฐ ์ ๋ ฅ
// ํธ์ถ ์๋จ
@Autowired(required = false)
public void setNoBean1(Member member) {
System.out.println("setNoBean1 = " + member);
}
//null ํธ์ถ
@Autowired
public void setNoBean2(@Nullable Member member) {
System.out.println("setNoBean2 = " + member);
}
//Optional.empty ํธ์ถ
@Autowired(required = false)
public void setNoBean3(Optional<Member> member) {
System.out.println("setNoBean3 = " + member);
}
โ ์์ฑ์ ์ฃผ์ ์ final ํค์๋
- ์์ฑ์์์ ํน์๋ผ๋ ๊ฐ์ด ์ค์ ๋์ง ์๋ ์ค๋ฅ๋ฅผ ์ปดํ์ผ ์์ ์์ ๋ง์์ค
@Component
public class OrderServiceImpl implements OrderService {
private final MemberRepository memberRepository;
private final DiscountPolicy discountPolicy;
//...
}
โfinal ํค์๋
: ์์(constant)๋ฅผ ์ ์ธํ๋ ํค์๋
: ์์๋ ํ๋ฒ ์ ์ฅํ๋ฉด ๋ค๋ฅธ ๊ฐ์ผ๋ก ๋ณ๊ฒฝ X (์ ์ธ๊ณผ ๋์์ ์ด๊ธฐํ ๊ถ์ฅ!)
final int PI = 3.14
๐ ๋กฌ๋ณต ๋ผ์ด๋ธ๋ฌ๋ฆฌ
- ๋กฌ๋ณต ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ์ ๊ณตํ๋ @RequiredArgsConstructor ๊ธฐ๋ฅ์ ์ฌ์ฉํ๋ฉด final์ด ๋ถ์ ํ๋๋ฅผ ๋ชจ์์ ์์ฑ์๋ฅผ ์๋์ผ๋ก ๋ง๋ค์ด ์ค
0๏ธโฃ build.gradle ์์
//lombok ์ค์ ์ถ๊ฐ ์์
configurations {
compileOnly {
extendsFrom annotationProcessor
}
}
//lombok ์ค์ ์ถ๊ฐ ๋
dependencies {
implementation 'org.springframework.boot:spring-boot-starter'
//lombok ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ถ๊ฐ ์์
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
testCompileOnly 'org.projectlombok:lombok'
testAnnotationProcessor 'org.projectlombok:lombok'
//lombok ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ถ๊ฐ ๋
testImplementation('org.springframework.boot:spring-boot-starter-test') {
exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
}
}
- Preferences โก๏ธ plugin โก๏ธ lombok ๊ฒ์ ์ค์น ์คํ (์ฌ์์)
- Preferences โก๏ธ Annotation Processors โก๏ธ Enable annotation processing ์ฒดํฌ (์ฌ์์)
1๏ธโฃ ์ฝ๋ ์์
- ๊ธฐ์กด
@Component
public class OrderServiceImpl implements OrderService {
private final MemberRepository memberRepository;
private final DiscountPolicy discountPolicy;
public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy discountPolicy) {
this.memberRepository = memberRepository;
this.discountPolicy = discountPolicy;
}
}
- ๋กฌ๋ณต ์ ์ฉ ํ
@Component
@RequiredArgsConstructor
public class OrderServiceImpl implements OrderService {
private final MemberRepository memberRepository;
private final DiscountPolicy discountPolicy;
}
๐ก ์ต๊ทผ์๋ ์์ฑ์๋ฅผ ๋ฑ 1๊ฐ ๋๊ณ , @Autowired๋ฅผ ์๋ตํ๋ ๋ฐฉ๋ฒ์ ์ฃผ๋ก ์ฌ์ฉ. ์ฌ๊ธฐ์ Lombok ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ @RequiredArgsConstructorํจ๊ป ์ฌ์ฉํ๋ฉด ๊ธฐ๋ฅ์ ๋ค ์ ๊ณตํ๋ฉด์, ์ฝ๋๋ ๊น๋ํ๊ฒ ์ฌ์ฉํ ์ ์์!
โ ์กฐํ ๋น์ด 2๊ฐ ์ด์์ผ ๋
: @Autowired๋ ํ์ (Type)์ผ๋ก ์กฐํํ๊ธฐ ๋๋ฌธ์ ์ ํ๋ ๋น์ด 2๊ฐ ์ด์์ด๋ฉด NoUniqueBeanDefinitionException ์ค๋ฅ ๋ฐ์!
→ ์ด ๋ฌธ์ ๋ฅผ ํด๊ฒฐํด๋ณด์!
1๏ธโฃ @Autowired ํ๋ ๋ช ๋งค์นญ
: @Autowired๋ ํ์ ๋งค์นญ์ ์๋ํ๊ณ , ์ด๋ ์ฌ๋ฌ ๋น์ด ์์ผ๋ฉด ํ๋ ์ด๋ฆ, ํ๋ผ๋ฏธํฐ ์ด๋ฆ์ผ๋ก ๋น ์ด๋ฆ์ ์ถ๊ฐ ๋งค์นญํจ
// ๊ธฐ์กด ์ฝ๋
@Autowired
private DiscountPolicy discountPolicy
// ํ๋ ๋ช
์ ๋น ์ด๋ฆ์ผ๋ก ๋ณ๊ฒฝ
@Autowired
private DiscountPolicy rateDiscountPolicy
2๏ธโฃ @Qualifier ์ฌ์ฉ
: ์ถ๊ฐ ๊ตฌ๋ถ์๋ฅผ ๋ถ์ฌ์ฃผ๋ ๋ฐฉ๋ฒ. ์ฃผ์ ์ ์ถ๊ฐ์ ์ธ ๋ฐฉ๋ฒ์ ์ ๊ณตํ๋ ๊ฒ์ด์ง ๋น ์ด๋ฆ์ ๋ณ๊ฒฝํ๋ ๊ฒ์ ์๋
@Component
@Qualifier("mainDiscountPolicy")
public class RateDiscountPolicy implements DiscountPolicy {}
@Autowired
public OrderServiceImpl(MemberRepository memberRepository,
@Qualifier("mainDiscountPolicy") DiscountPolicy discountPolicy) {
this.memberRepository = memberRepository;
this.discountPolicy = discountPolicy;
}
- @Qualifier๋ผ๋ฆฌ ๋งค์นญ
- ๋น ์ด๋ฆ ๋งค์นญ (mainDiscountPolicy๋ผ๋ ์คํ๋ง ๋น์ ์ถ๊ฐ๋ก ๊ฒ์)
- NoSuchBeanDefinitionException ์์ธ ๋ฐ์
3๏ธโฃ @Primary ์ฌ์ฉ
: ์ฐ์ ์์๋ฅผ ์ ํ๋ ๋ฐฉ๋ฒ. @Autowired ์์ ์ฌ๋ฌ ๋น์ด ๋งค์นญ๋๋ฉด @Primary๊ฐ ์ฐ์ ๊ถ์ ๊ฐ์ง.
๐ก ์ฝ๋์์ ๋ฉ์ธ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ปค๋ฅ์ ์ ํ๋ํ๋ ์คํ๋ง ๋น์ด ์๊ณ , ๊ฐ๋ ์ฌ์ฉํ๋ ์๋ธ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ปค๋ฅ์ ์ ํ๋ํ๋ ์คํ๋ง ๋น์ด ์๋ค๊ณ ํ๋ค๋ฉด, ๋ฉ์ผ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ปค๋ฅ์ ์ ํ๋ํ๋ ์คํ๋ง ๋น์ @Primary๋ฅผ ์ ์ฉํ๊ณ , ์๋ธ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ปค๋ฅ์ ๋น์ ํ๋ํ ๋๋ @Qualifier๋ฅผ ์ง์ ํด์ ๋ช ์์ ์ผ๋ก ํ๋ํ๋ ๋ฐฉ์์ ์ทจํ๋ฉด ์ข๋ค! (์ฐ์ ์์๋ @Primary๋ณด๋ค๋ @Qualifier๊ฐ ๋ ๋๋ค)
โ ์ ๋ ธํ ์ด์ ์ ์ง์ ๋ง๋ค์ด ๋ณด์
: @Qualifier("mainDisxountPolicy")์ด๋ ๊ฒ ๋ฌธ์๋ฅผ ์ ์ผ๋ฉด ์ปดํ์ผ์ ํ์ ์ฒดํฌ๊ฐ ๋์ง ์์!
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER,ElementType.TYPE, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Qualifier("mainDiscountPolicy")
public @interface MainDiscountPolicy {
}
@Component
@MainDiscountPolicy
public class RateDiscountPolicy implements DiscountPolicy {}
- ์ ๋ ธํ ์ด์ ์๋ ์์์ด๋ผ๋ ๊ฐ๋ X. ์ฌ๋ฌ ์ ๋ ธํ ์ด์ ์ ๋ชจ์์ ์ฌ์ฉํ๋ ๊ธฐ๋ฅ์ ์คํ๋ง์ด ์ง์ํด์ฃผ๋ ๊ธฐ๋ฅ!
โ ์กฐํํ ๋น์ด ๋ชจ๋ ํ์ํ ๋
public class AllBeanTest {
@Test
void findAllBean() {
ApplicationContext ac = new AnnotationConfigApplicationContext(AutoAppConfig.class, DiscountService.class);
DiscountService discountService = ac.getBean(DiscountService.class);
Member member = new Member(1L, "userA", Grade.VIP);
int discountPrice = discountService.discount(member, 10000,
"fixDiscountPolicy");
assertThat(discountService).isInstanceOf(DiscountService.class);
assertThat(discountPrice).isEqualTo(1000);
}
static class DiscountService {
private final Map<String, DiscountPolicy> policyMap;
private final List<DiscountPolicy> policies;
public DiscountService(Map<String, DiscountPolicy> policyMap,
List<DiscountPolicy> policies) {
this.policyMap = policyMap;
this.policies = policies;
System.out.println("policyMap = " + policyMap);
System.out.println("policies = " + policies);
}
public int discount(Member member, int price, String discountCode) {
DiscountPolicy discountPolicy = policyMap.get(discountCode);
System.out.println("discountCode = " + discountCode);
System.out.println("discountPolicy = " + discountPolicy);
return discountPolicy.discount(member, price);
}
}
}
- new AnnotationConfigApplicationContext()๋ฅผ ํตํด ์คํ๋ง ์ปจํ ์ด๋ ์์ฑ!
- AutoAppConfig.class, DiscountService.class๋ฅผ ํ๋ผ๋ฏธํฐ๋ก ๋๊ธฐ๋ฉด์ ํด๋น ํด๋์ค๋ฅผ ์๋์ผ๋ก ์คํ๋ง ๋น์ผ๋ก ๋ฑ๋ก
→ ๊ทธ๋์ Map<String, DiscountPolicy>์ ํค์ ์คํ๋ง ๋น์ ์ด๋ฆ, ๊ฐ์ผ๋ก DiscountPolicy ํ์ ์ผ๋ก ์กฐํํ ๋ชจ๋ ์คํ๋ง ๋น์ ๋ด์ ์ ์๋ ๊ฒ!
๐๐ปโ๏ธ ๊ทธ๋ ๋ค๋ฉด ์ธ์ ์๋ ๋น ๋ฑ๋ก์ ์ฌ์ฉํ๊ณ , ์ธ์ ์๋ ๋น ๋ฑ๋ก์ ์ฌ์ฉํด์ผ ํ ๊น??
๐ก ํธ๋ฆฌํ ์๋ ๊ธฐ๋ฅ์ ๊ธฐ๋ณธ์ผ๋ก ์ฌ์ฉํ๊ณ ์ง์ ๋ฑ๋กํ๋ ๊ธฐ์ ์ง์ ๊ฐ์ฒด๋ ์๋ ๋ฑ๋กํ์! ๊ทธ๋ฆฌ๊ณ ๋คํ์ฑ์ ์ ๊ทน ํ์ฉํ๋ ๋น์ฆ๋์ค ๋ก์ง์ ์๋ ๋ฑ๋ก์ ๊ณ ๋ฏผํด๋ณด์!
๐ ๋น ์๋ช ์ฃผ๊ธฐ ์ฝ๋ฐฑ
: ์คํ๋ง ์ปจํ ์ด๋ ์์ฑ โก๏ธ ์คํ๋ง ๋น ์์ฑ โก๏ธ ์์กด๊ด๊ณ ์ฃผ์ โก๏ธ ์ด๊ธฐํ ์ฝ๋ฐฑ โก๏ธ ์ฌ์ฉ โก๏ธ ์๋ฉธ ์ ์ฝ๋ฐฑ โก๏ธ ์คํ๋ง ์ข ๋ฃ
1๏ธโฃ ์ธํฐํ์ด์ค(InitalizingBean, DisposableBean) ๊ตฌํ
public class NetworkClient implements InitializingBean, DisposableBean {
private String url;
public NetworkClient() {
System.out.println("์์ฑ์ ํธ์ถ, url = " + url);
}
public void setUrl(String url) {
this.url = url;
}
//์๋น์ค ์์์ ํธ์ถ
public void connect() {
System.out.println("connect: " + url);
}
public void call(String message) {
System.out.println("call: " + url + " message = " + message);
}
//์๋น์ค ์ข
๋ฃ์ ํธ์ถ
public void disConnect() {
System.out.println("close + " + url);
}
@Override
public void afterPropertiesSet() throws Exception {
connect();
call("์ด๊ธฐํ ์ฐ๊ฒฐ ๋ฉ์์ง");
}
@Override
public void destroy() throws Exception {
disConnect();
}
}
- ์คํ๋ง ์ ์ฉ ์ธํฐํ์ด์ค(์ฝ๋๊ฐ ์คํ๋ง ์ ์ฉ ์ธํฐํ์ด์ค์ ์์กด)
- ์ด๊ธฐํ, ์๋ฉธ ๋ฉ์๋์ ์ด๋ฆ์ ๋ณ๊ฒฝX
- ์ธ๋ถ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ์ ์ฉX
2๏ธโฃ ์ค์ ์ ๋ณด์ ์ด๊ธฐํ ๋ฉ์๋, ์ข ๋ฃ ๋ฉ์๋ ์ง์
@Configuration
static class LifeCycleConfig {
@Bean(initMethod = "init", destroyMethod = "close")
public NetworkClient networkClient() {
NetworkClient networkClient = new NetworkClient();
networkClient.setUrl("http://hello-spring.dev");
return networkClient;
}
}
- ๋ฉ์๋ ์ด๋ฆ์ ์์ ๋กญ๊ฒ ์ค ์ ์์
- ์คํ๋ง ๋น์ด ์คํ๋ง ์ฝ๋์ ์์กดX
- ์ธ๋ถ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์๋ ์ด๊ธฐํ, ์ข ๋ฃ ๋ฉ์๋๋ฅผ ์ ์ฉO
โ ์ข ๋ฃ ๋ฉ์๋ ์ถ๋ก
- @Bean์ destroyMethod๋ ๊ธฐ๋ณธ๊ฐ์ด (inferred)(์ถ๋ก )์ผ๋ก ๋ฑ๋ก
- close, shutdown๋ผ๋ ์ด๋ฆ์ ๋ฉ์๋๋ฅผ ์๋์ผ๋ก ํธ์ถ
- ์ถ๋ก ๊ธฐ๋ฅ์ ์ฌ์ฉํ๊ธฐ ์ซ์ผ๋ฉด destroyMethod="" ์ฒ๋ผ ๋น ๊ณต๋ฐฑ์ ์ง์
3๏ธโฃ @PostConstruct, @PreDestroy ์ ๋ ธํ ์ด์ ์ง์ (๊ถ์ฅ)
public class NetworkClient {
// ...
@PostConstruct
public void init() {
System.out.println("NetworkClient.init"); connect();
call("์ด๊ธฐํ ์ฐ๊ฒฐ ๋ฉ์์ง");
}
@PreDestroy
public void close() {
System.out.println("NetworkClient.close");
disConnect();
}
}
- ์คํ๋ง์ ์ข ์์ ์ธ ๊ธฐ์ ์ด ์๋๋ผ ์๋ฐ ํ์ค (์คํ๋ง์ด ์๋ ๋ค๋ฅธ ์ปจํ ์ด๋์์๋ ๋์)
- ์ปดํฌ๋ํธ ์ค์บ๊ณผ ์ ์ด์ธ๋ฆผ
- ์ธ๋ถ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ด๊ธฐํ, ์ข ๋ฃํด์ผ ํ๋ฉด @Bean์ initMethod, destroyMethod๋ฅผ ์ฌ์ฉ!
๐ ๋น ์ค์ฝํ
โ๏ธ ์ฑ๊ธํค : ๊ธฐ๋ณธ ์ค์ฝํ, ์คํ๋ง ์ปจํ ์ด๋์ ์์๊ณผ ์ข ๋ฃ๊น์ง ์ ์ง๋๋ ๊ฐ์ฅ ๋์ ๋ฒ์์ ์ค์ฝํ
โ๏ธ ํ๋กํ ํ์
: ์คํ๋ง ์ปจํ ์ด๋๋ ํ๋กํ ํ์ ๋น์ ์์ฑ๊ณผ ์์กด๊ด๊ณ ์ฃผ์ ๊น์ง๋ง ๊ด์ฌํ๊ณ ๋๋ ๊ด๋ฆฌํ์ง ์๋ ๋งค์ฐ ์งง์ ๋ฒ์์ ์ค์ฝํ
- ์คํ๋ง ์ปจํ ์ด๋์ ์์ฒญํ ๋ ๋ง๋ค ์๋ก ์์ฑ
- ์ข ๋ฃ ๋ฉ์๋ ํธ์ถX
โ์ฑ๊ธํค ๋น๊ณผ ํจ๊ป ์ฌ์ฉํ๋ฉด ํ๋กํ ํ์ ๋น์ด ์ฑ๊ธํค ๋น์ฒ๋ผ ๋์ - ๋ฌธ์ !!
๐ก ์ฐ๋ฆฌ๋ ์ฑ๊ธํค ๋น๊ณผ ํ๋กํ ํ์ ๋น์ ํจ๊ป ์ฌ์ฉํ ๋, ํญ์ ์๋ก์ด ํ๋กํ ํ์ ๋น์ ์์ฑํด์ผ ํ๋ค! Provider๋ก ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ ์ ์๋ค!
@Autowired
private ObjectProvider<PrototypeBean> prototypeBeanProvider;
public int logic() {
PrototypeBean prototypeBean = prototypeBeanProvider.getObject(); // DL
prototypeBean.addCount();
int count = prototypeBean.getCount();
return count;
}
- DL(Dependency Lookup) : ์์กด๊ด๊ณ๋ฅผ ์ธ๋ถ์์ ์ฃผ์ (DI) ๋ฐ๋๊ฒ ์๋๋ผ ์ง์ ํ์ํ ์์กด๊ด๊ณ๋ฅผ ์ฐพ๋ ๊ฒ
- ObjectProvider : ObjectFactory ์์, ์ต์ , ์คํธ๋ฆผ ์ฒ๋ฆฌ ๋ฑ ํธ์ ๊ธฐ๋ฅ์ด ๋ง๊ณ , ๋ณ๋์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ํ์X. ์คํ๋ง์ ์์กด
โ ์ด์ธ์๋ ์๋ฐ ํ์ค์ธ JSR-330 Provider๊ฐ ์์ง๋ง, ์ฝ๋๋ฅผ ์คํ๋ง์ด ์๋ ๋ค๋ฅธ ์ปจํ ์ด๋์์๋ ์ฌ์ฉํ๋๊ฒ ์๋๋ผ๋ฉด ObjectProvider๋ฅผ ์ฌ์ฉํ์!
โ๏ธ ์น ๊ด๋ จ ์ค์ฝํ
- ์น ํ๊ฒฝ์์๋ง ๋์
- ์คํ๋ง์ด ํด๋น ์ค์ฝํ์ ์ข ๋ฃ์์ ๊น์ง ๊ด๋ฆฌ(์ข ๋ฃ ๋ฉ์๋๊ฐ ํธ์ถ)
- ์น ์ค์ฝํ ์ข ๋ฅ
1) request : ์น ์์ฒญ์ด ๋ค์ด์ค๊ณ ๋๊ฐ ๋๊น์ง ์ ์ง๋๋ ์ค์ฝํ
2) session : ์น ์ธ์ ์ด ์์ฑ๋๊ณ ์ข ๋ฃ๋ ๋๊น์ง ์ ์ง๋๋ ์ค์ฝํ
3) application : ์น์ ์๋ธ๋ฆฟ ์ปจํ ์คํธ์ ๊ฐ์ ๋ฒ์๋ก ์ ์ง๋๋ ์ค์ฝํ
// ์ปดํฌ๋ํธ ์ค์บ ์๋ ๋ฑ๋ก
@Scope("prototype")
@Component
public class HelloBean {}
// ์๋ ๋ฑ๋ก
@Scope("prototype")
@Bean
PrototypeBean HelloBean() {
return new HelloBean();
}
โ ์น ์์ฒญ(request)์ด ์ค๋ฉด ๋ก๊ทธ๋ฅผ ๋จ๊ธฐ๋ ์์ ๋ฅผ ๊ฐ๋ฐํด๋ณด์!
0๏ธโฃ web ํ๊ฒฝ์ด ๋์ํ๋๋ก build.gradle์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ถ๊ฐ
//web ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ถ๊ฐ
implementation 'org.springframework.boot:spring-boot-starter-web'
1๏ธโฃ MyLogger ํด๋์ค ๋ง๋ค๊ธฐ
@Component
@Scope(value = "request")
public class MyLogger {
private String uuid;
private String requestURL;
public void setRequestURL(String requestURL) {
this.requestURL = requestURL;
}
public void log(String message) {
System.out.println("[" + uuid + "]" + "[" + requestURL + "] " + message);
}
@PostConstruct
public void init() {
uuid = UUID.randomUUID().toString();
System.out.println("[" + uuid + "] request scope bean create:" + this);
}
@PreDestroy
public void close() {
System.out.println("[" + uuid + "] request scope bean close:" + this);
}
}
- requestURL์ ์ด ๋น์ด ์์ฑ๋๋ ์์ ์๋ ์ ์ ์์ผ๋ฏ๋ก, ์ธ๋ถ์์ setter๋ก ์ ๋ ฅ ๋ฐ์!
2๏ธโฃ LogDemoController ํด๋์ค ๋ง๋ค๊ธฐ
@Controller
@RequiredArgsConstructor
public class LogDemoController {
private final LogDemoService logDemoService;
private final MyLogger myLogger;
@RequestMapping("log-demo")
@ResponseBody
public String logDemo(HttpServletRequest request) {
String requestURL = request.getRequestURL().toString();
myLogger.setRequestURL(requestURL);
myLogger.log("controller test");
logDemoService.logic("testId");
return "OK";
}
}
๐ก requestURL์ MyLogger์ ์ ์ฅํ๋ ๋ถ๋ถ์ ์ปจํธ๋กค๋ฌ ๋ณด๋ค๋ ๊ณตํต ์ฒ๋ฆฌ๊ฐ ๊ฐ๋ฅํ ์คํ๋ง ์ธํฐ์ ํฐ๋ ์๋ธ๋ฆฟ ํํฐ ๊ฐ์ ๊ณณ์ ํ์ฉํ๋ ๊ฒ์ด ์ข์!
3๏ธโฃ LogDemoService ํด๋์ค ๋ง๋ค๊ธฐ
@Service
@RequiredArgsConstructor
public class LogDemoService {
private final MyLogger myLogger;
public void logic(String id) {
myLogger.log("service id = " + id);
}
}
- ์๋น์ค ๊ณ์ธต์ ์น ๊ธฐ์ ์ ์ข ์๋์ง ์๊ณ , ๊ฐ๊ธ์ ์์ํ๊ฒ ์ ์งํ๋ ๊ฒ์ด ์ ์ง๋ณด์ ๊ด์ ์์ ์ข์(์น๊ณผ ๊ด๋ จ๋ ๋ถ๋ถ์ ์ปจํธ๋กค๋ฌ๊น์ง๋ง!)
- ์น๊ณผ ๊ด๋ จ๋ ์ ๋ณด๋ฅผ request scope์ MyLogger์ ๋ฉค๋ฒ๋ณ์์ ์ ์ฅํด์ ์ฝ๋์ ๊ณ์ธต์ ๊น๋ํ๊ฒ ์ ์ง!
โ ์ ํ๋ฆฌ์ผ์ด์ ์ ์คํํ๋ ์์ ์์ ์ฑ๊ธํค ๋น์ ์์ฑํด์ ์ฃผ์ ์ด ๊ฐ๋ฅํ์ง๋ง, request ์ค์ฝํ ๋น์ ์์ง ์์ฑX(์ค์ ๊ณ ๊ฐ์ ์์ฒญ์ด ์์ผ ์์ฑ ๊ฐ๋ฅ!)
โก๏ธ ์ค๋ฅ ๋ฐ์
โ ์ด ๋ฌธ์ ๋ฅผ ํด๊ฒฐํด๋ณด์!
โ๏ธ Provider ์ฌ์ฉ
@Controller
@RequiredArgsConstructor
public class LogDemoController {
private final LogDemoService logDemoService;
private final ObjectProvider<MyLogger> myLoggerProvider; // ๋ฌ๋ผ์ง ๋ถ๋ถ
@RequestMapping("log-demo")
@ResponseBody
public String logDemo(HttpServletRequest request) {
String requestURL = request.getRequestURL().toString();
MyLogger myLogger = myLoggerProvider.getObject(); // ๋ฌ๋ผ์ง ๋ถ๋ถ
myLogger.setRequestURL(requestURL);
myLogger.log("controller test");
logDemoService.logic("testId");
return "OK";
}
}
@Service
@RequiredArgsConstructor
public class LogDemoService {
private final ObjectProvider<MyLogger> myLoggerProvider; // ๋ฌ๋ผ์ง ๋ถ๋ถ
public void logic(String id) {
MyLogger myLogger = myLoggerProvider.getObject(); // ๋ฌ๋ผ์ง ๋ถ๋ถ
myLogger.log("service id = " + id);
}
}
- ObjectProvider ๋๋ถ์ ObjectProvider.getObject()๋ฅผ ํธ์ถํ๋ ์์ ๊น์ง request scope ๋น์ ์์ฑ์ ์ง์ฐํ ์ ์์
- ObjectProvider.getObject()๋ฅผ LogDemoController, LogDemoService์ ๊ฐ๊ฐ ํ๋ฒ์ฉ ๋ฐ๋ก ํธ์ถํด๋ ๊ฐ์ HTTP ์์ฒญ์ด๋ฉด ๊ฐ์ ์คํ๋ง ๋น์ด ๋ฐํ!
โ๏ธ ํ๋ก์ ์ฌ์ฉ
@Component
@Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS) // ํต์ฌ!
public class MyLogger {
// ...
}
- ์ ์ฉ ๋์์ด ํด๋์ค๋ฉด TARGET_CLASS, ์ธํฐํ์ด์ค๋ฉด INTERFACES๋ฅผ ์ ํ
- MyLogger์ ๊ฐ์ง ํ๋ก์ ํด๋์ค๋ฅผ ๋ง๋ค์ด๋๊ณ HTTP request์ ์๊ด ์์ด ๊ฐ์ง ํ๋ก์ ํด๋์ค๋ฅผ ๋ค๋ฅธ ๋น์ ๋ฏธ๋ฆฌ ์ฃผ์ ํด ๋ ์ ์์!
๐๐ปโ๏ธ ์ด๊ฒ ์ด๋ป๊ฒ ๊ฐ๋ฅํ๊ฑธ๊น?
: CGLIB(๋ฐ์ดํธ์ฝ๋๋ฅผ ์กฐ์ํ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ)๋ก ๋ด ํด๋์ค(MyLogger)๋ฅผ ์์ ๋ฐ๋ ๊ฐ์ง ํ๋ก์ ๊ฐ์ฒด(ex. MyLogger$$EnhancerBySpringCGLIB)๋ฅผ ๋ง๋ค์ด์ ์ฃผ์
: ์คํ๋ง ์ปจํ ์ด๋์ "myLogger"๋ผ๋ ์ด๋ฆ ๋์ ์ ์ด ๊ฐ์ง ํ๋ก์ ๊ฐ์ฒด๋ฅผ ๋ฑ๋ก
: ac.getBean("myLogger", MyLogger.class)๋ก ์กฐํํด๋ ํ๋ก์ ๊ฐ์ฒด๊ฐ ์กฐํ๋จ
โ๏ธ ๊ฐ์ง ํ๋ก์ ๊ฐ์ฒด๋ ์์ฒญ์ด ์ค๋ฉด ๊ทธ๋ ๋ด๋ถ์์ ์ง์ง ๋น์ ์์ฒญํ๋ ์์ ๋ก์ง์ด ๋ค์ด์์
- ๊ฐ์ง ํ๋ก์ ๊ฐ์ฒด๋ ๋ด๋ถ์ ์ง์ง myLogger๋ฅผ ์ฐพ๋ ๋ฐฉ๋ฒ์ ์๊ณ ์์
- ํด๋ผ์ด์ธํธ๊ฐ myLogger.log()์ ํธ์ถํ๋ฉด ์ฌ์ค์ ๊ฐ์ง ํ๋ก์ ๊ฐ์ฒด์ ๋ฉ์๋๋ฅผ ํธ์ถํ ๊ฒ
- ๊ฐ์ง ํ๋ก์ ๊ฐ์ฒด๋ request ์ค์ฝํ์ ์ง์ง myLogger.log()๋ฅผ ํธ์ถ
- ๊ฐ์ง ํ๋ก์ ๊ฐ์ฒด๋ ์๋ณธ ํด๋์ค๋ฅผ ์์ ๋ฐ์์ ๋ง๋ค์ด์ก๊ธฐ ๋๋ฌธ์ ์ด ๊ฐ์ฒด๋ฅผ ์ฌ์ฉํ๋ ํด๋ผ์ด์ธํธ ์ ์ฅ์์๋ ์ฌ์ค ์๋ณธ์ธ์ง ์๋์ง๋ ๋ชจ๋ฅด๊ฒ, ๋์ผํ๊ฒ ์ฌ์ฉํ ์ ์๋ค(๋คํ์ฑ)
'๋ฐฑ์๋ > Spring' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
์คํ๋ง ๋ถํธ์ JPA ํ์ฉ1 (0) | 2023.05.17 |
---|---|
HTTP ์น ๊ธฐ๋ณธ ์ง์ (0) | 2023.04.29 |
[์คํ๋ง ํต์ฌ ์๋ฆฌ] ๊ธฐ๋ณธํธ1 (0) | 2023.04.06 |
[์คํ๋ง ํต์ฌ ์๋ฆฌ] ๊ธฐ๋ณธํธ - ๊ฐ์ฒด ์งํฅ ์ค๊ณ์ ์คํ๋ง (0) | 2023.04.05 |
[์คํ๋ง ์ ๋ฌธ] ์ฝ๋๋ก ๋ฐฐ์ฐ๋ ์คํ๋ง ๋ถํธ, ์น MVC, DB์ ๊ทผ ๊ธฐ์ (0) | 2023.03.21 |