public class Car {
Tire tire;
public Car() {
tire = new KoreaTire();
}
public void introduceTire() {
tire.introduce();
}
}
interface Tire {
void introduce();
}
class KoreaTire() {
public void introduce() {
System.out.println("๋๋ ํ๊ตญ ํ์ด์ด์ผ");
}
}
class AmericaTire() {
public void introduce() {
System.out.println("๋๋ ๋ฏธ๊ตญ ํ์ด์ด์ผ");
}
}์์ ์ฝ๋์ ๊ฐ์ด Car ํด๋์ค๋ Tire ํด๋์ค์ ๋ฉ์๋๋ฅผ ์ฌ์ฉํ๊ณ ์๋ค. ์ด๋ฅผ Car ํด๋์ค๊ฐ Tire ํด๋์ค๋ฅผ ์์กดํ๊ณ ์๋ค๊ณ ๋งํ๋ค.
ํ์ง๋ง Car ํด๋์ค๋ ๋ณธ์ธ์ ๋ก์ง์ ๋ํ ์ฑ ์๋ฟ๋ง ์๋๋ผ Tire ํด๋์ค์ ๊ตฌํ์ ๋ํ ์ฑ ์ ๋ํ ๊ฐ์ง๊ณ ์๋ ๊ฒ์ด๋ค (= ๋ ํด๋์ค์ ๊ฒฐํฉ๋๊ฐ ๊ฐํ๋ค)
โ Car ํด๋์ค๋ Tire ํด๋์ค์ ๊ตฌํ ์ฑ ์์ ์ง๋ ๊ฒ์ด ๋ถ๋ด์ค๋ฌ์ ์ 3์์๊ฒ ์์กด๊ด๊ณ์ ๋ํ ์ฑ ์์ ์์ํ๊ณ ์ ํ๋ค. ์ด๋ฅผ IoC, ์ ์ด์ ์ญ์ ์ด๋ผ๊ณ ๋งํ๋ค.
public class Select() {
public Car car() {
return new Car();
}
public Tire tire() {
// return new KoreaTire();
return new AmericaTire();
}
}
public class Car {
Tire tire;
public Car() {
tire = new Select().tire();
}
// ...์ดํ ๋์ผ ..
}์์ ๊ฐ์ด Select๋ผ๋ ํด๋์ค๋ฅผ ๋ง๋ค์ด ์ด๋ค tire๋ฅผ ๋๊ฒจ์ค์ง์ ๋ํ ์ฑ ์์ ์์ํ๋ค.
Car ํด๋์ค๋ Select๊ฐ ๋ฐํํด์ค tire๋ง ์ฌ์ฉํ๊ธฐ ๋๋ฌธ์, ์ถํ์ KoreaTire๋ฅผ ์ฌ์ฉํ๊ณ ์ถ๋ค๊ณ ํด๋ Car ํด๋์ค๋ฅผ ๋ณ๊ฒฝํ๋ ๊ฒ์ด ์๋๋ผ Select ํด๋์ค์ tire์ ๋ํ return ๋ถ๋ถ๋ง ์์ ํด์ฃผ๋ฉด ๋๋ค.
์ด๋ ๊ฒ Car๋ Tire์ ์ฑ ์์ ๋ํ ๋ถ๋ฆฌ๋ฅผ ์ด๋ฃจ์๋ค!
๐จย ํ์ง๋ง ์์ง Select ํด๋์ค๋ Tire ์ ๋ํด์ ์ฑ ์์ ๋ถ๋ฆฌํ์ง ๋ชป ํ๊ฑฐ ์๋๊ฐ์??
์ฌ์ค ์คํ๋ง์์ ์ ๊ณตํ๋ IoC ์ปจํ ์ด๋๋ Select ํด๋์ค๊ฐ ๋ด๋นํ๊ณ ์๋ ์์กด์ฑ ๊ด๋ฆฌ์ ๊ฐ์ฒด ์ ํ ๊ธฐ๋ฅ์ ์๋์ผ๋ก ์ฒ๋ฆฌํด์ค๋ค.
๊ฐ์ฒด์ ์์ฑ๊ณผ ์์กด์ฑ ์ฃผ์ , ๊ด๋ฆฌ, ์๋ฉธ์ ๋ด๋นํ๋ ์ค์ ๊ด๋ฆฌ ์์คํ ์ด๋ค.
- ๋น(Bean) ๊ด๋ฆฌ
- IoC ์ปจํ ์ด๋๋ ์ ํ๋ฆฌ์ผ์ด์ ์์ ์ฌ์ฉ๋๋ ๊ฐ์ฒด(๋น)๋ฅผ ์์ฑํ๊ณ ๊ด๋ฆฌํ๋ค.
- ๊ฐ๋ฐ์๋ ๊ฐ์ฒด ์์ฑ๊ณผ ๊ด๋ฆฌ์ ๋ํ ๋ถ๋ถ์ ์ ๊ฒฝ ์ฐ์ง ์์๋ ๋๋ค.
- ์์กด์ฑ ์ฃผ์
(Dependency Injection)
- IoC ์ปจํ ์ด๋๋ ๋น ๊ฐ์ ์์กด์ฑ์ ๊ด๋ฆฌํ๊ณ ํ์ํ ์์กด์ฑ์ ์ฃผ์ ํ๋ค.
- โ ๊ฐ์ฒด ๊ฐ์ ๊ฒฐํฉ๋๋ฅผ ๋ฎ์ถ๊ณ , ์ฝ๋์ ์ฌ์ฌ์ฉ์ฑ๊ณผ ์ ์ง๋ณด์์ฑ์ ํฅ์์ํจ๋ค.
- ๋ผ์ดํ์ฌ์ดํด ๊ด๋ฆฌ
- IoC ์ปจํ ์ด๋๋ ๋น์ ๋ผ์ดํ์ฌ์ดํด์ ๊ด๋ฆฌํ๋ฉฐ, ์ด๊ธฐํ์ ์๋ฉธ ์์ ์ ์ฝ๋ฐฑ ๋ฉ์๋๋ฅผ ํธ์ถํ ์ ์๋ค.
- ์ค์ ๊ด๋ฆฌ
- Spring IoC ์ปจํ ์ด๋๋ ์ ํ๋ฆฌ์ผ์ด์ ์ค์ ์ ๊ด๋ฆฌํ๊ณ , XML, Java ์ค์ ํด๋์ค, ์ด๋ ธํ ์ด์ ๊ธฐ๋ฐ์ ์ค์ ์ ์ง์ํ๋ค.
IoC ์ปจํ ์ด๋๋ DI ํจํด์ ํตํด ๊ฐ์ฒด ๊ฐ์ ์์กด์ฑ์ ๊ด๋ฆฌํ๋๋ฐ
๊ทธ๋ผ ์ด์ DI ํจํด์ ๋ํด ์์๋ณด์!
๊ฐ์ฒด๊ฐ์ ์์กด์ฑ์ ์ธ๋ถ์์ ์ฃผ์ ํ๋ ๊ฒ์ ๋งํ๋ฉฐ, ๊ตฌ์ฒด์ ์ธ ์์กด ์ค๋ธ์ ํธ์ ๊ทธ๊ฒ์ ์ฌ์ฉํ ์ฃผ์ฒด๋ฅผ ๋ฐํ์ ์์ ์ฐ๊ฒฐํด์ฃผ๋ ์์ ์ ๋งํ๋ค.
์์กด์ฑ์ ์ฃผ์ ํ๋ ์ธ๊ฐ์ง ๋ฐฉ๋ฒ์ด ์๋ค.
1) ์์ฑ์ ์ฃผ์
- ๊ฐ์ฒด๊ฐ ์์ฑ๋ ๋ ๋ชจ๋ ์์กด์ฑ์ด ์ฃผ์ ๋์ด ๋ถ๋ณ์ฑ์ ์ ์งํ ์ ์๋ค๋ ์ฅ์ ์ด ์๋ค.
@Component
public class Car {
private Tire tire;
@Autowired
public Car(@Qualifier("koreaTire") Tire tire) {
this.tire = tire;
}
}@Component
@RequiredArgsConstructor
public class Car {
private final Tire tire;
}์ฒซ๋ฒ์งธ ์ฝ๋์ ๋๋ฒ์งธ ์ฝ๋๋ ๋ชจ๋ ์์ฑ์ ์ฃผ์
์ ์์ ์ฝ๋์ธ๋ฐ @RequiredArgsConstructor ์ด๋
ธํ
์ด์
์ ์ฌ์ฉํ๋ฉด final๋ก ์ ์ธํ ํ๋์ @NonNull ์ด๋
ธํ
์ด์
์ด ๋ถ์ ํ๋์ ๋ํด์ ์์ฑ์๋ฅผ ์๋์ผ๋ก ๋ง๋ค์ด์ค๋ค.
2) Setter ์ฃผ์
- ์ ํ์ ์์กด์ฑ์ด ํ์ํ ๊ฒฝ์ฐ์ ์ฌ์ฉํ๊ธฐ ์ข๊ณ , ๊ฐ์ฒด ์์ฑ ์ดํ์๋ ์์กด์ฑ์ ๋ณ๊ฒฝํ ์ ์๋ ์ ์ฐ์ฑ์ ์ ๊ณตํ๋ค.
- ์์กด์ฑ์ด ์ฃผ์ ๋์ง ์์ ์ํ๋ก๋ ๊ฐ์ฒด ์์ฑ์ด ๊ฐ๋ฅํ๋ฏ๋ก ์์กด์ฑ ์ฃผ์ ์ด ๊ฐ์ ๋์ง ์๋ ๋ค๋ ๋จ์ ์ด ์๋ค.
public class Car {
private Tire tire;
// Setter ์ฃผ์
public void setTire(Tire tire) {
this.tire = tire;
}
}// ..์ธ๋ถ ์ฝ๋..
Tire tire = new KoreaTire();
Car car = new Car();
car.setTire(tire); // setter ์ฃผ์
3) ํ๋ ์ฃผ์
- ์คํ๋ง์ด ์ง์ ํ๋์ ์ฃผ์ ํ๋ ๋ฐฉ์์ด๋ค.
- ์์กด์ฑ์ด ์ธ๋ถ์ ๋ช ํํ๊ฒ ๋๋ฌ๋์ง ์๊ธฐ ๋๋ฌธ์ ๊ฐ์ฒด์ ์์กด์ฑ์ ํ์ ํ๊ธฐ ํ๋ค๋ค๋ ๋จ์ ์ด ์๋ค.
@Component
public class Car {
@Autowired
@Qualifier("koreaTire")
private Tire tire;
}@Autowired ์ด๋
ธํ
์ด์
์ ์ฌ์ฉํ์ฌ ํ๋ ์ฃผ์
์ ์ํํฉ๋๋ค. Car ๊ฐ์ฒด๊ฐ ์์ฑ๋ ๋ tire๋ ์๋์ผ๋ก ์คํ๋ง IoC ์ปจํ
์ด๋์ ์ํด ์ฃผ์
๋๋ค.
koreaTire, americaTire์ ๊ฐ์ด ๊ฐ์ ํ์
์ ๋น์ด ์์ ๋, ๋ด๊ฐ ์ํ๋ ๋น์ ์ฃผ์
ํ๊ธฐ ์ํด @Qualifier ์ด๋
ธํ
์ด์
์ ์ฌ์ฉํ์ฌ ์ฃผ์
ํ๊ณ ์ ํ๋ ๋น์ ์ด๋ฆ์ ๋ช
์ํด์ค๋ค.
๐กย ์ธ๊ฐ์ง ๋ฐฉ๋ฒ ์ค ์์ฑ์ ์ฃผ์ ์ ๊ถ์ฅํ๋ค.
- ๋ถ๋ณ์ฑ ์ ์ง
- ์์ฑ์ ์ฃผ์ ์ ๊ฐ์ฒด๊ฐ ์์ฑ๋ ๋ ๋ชจ๋ ์์กด์ฑ์ด ๋ฐ๋์ ์ ๊ณต๋๋ค.
- ๊ฐ์ฒด ์์ฑ ํ ๋ณ๊ฒฝ๋์ง ์์์ ๋ณด์ฅํ๊ณ , ๊ฐ์ฒด์ ๋ถ๋ณ์ฑ์ ์ ์งํ ์ ์๋ค.
- ํ์ ์์กด์ฑ ์ฃผ์
๋ณด์ฅ
- ๊ฐ์ฒด๊ฐ ์์ฑ๋ ๋, ๋ชจ๋ ์์กด์ฑ์ ๋ชจ๋ ๋ฐ์์ผ ํ๊ธฐ ๋๋ฌธ์ ์์กด์ฑ์ ๋นผ๋จน์ ๊ฐ๋ฅ์ฑ์ด ์๋ค.
- (setter ์ฃผ์ ๊ฐ์ ๊ฒฝ์ฐ๋ setter ๋ฉ์๋๋ฅผ ํธ์ถํ์ง ์๊ณ ๋ ๊ฐ์ฒด ์์ฑ์ด ๊ฐ๋ฅํ์ฌ ์์กด์ฑ์ ๋นผ๋จน์ ์ ์๋ค)
- ํ
์คํธ ์ฉ์ด์ฑ
- ์์กด์ฑ์ ์ฃผ์ ๋ฐ๋ ๊ฐ์ฒด๋ฅผ ์ฝ๊ฒ ๋ชจํนํ ์ ์๋ค.
์ด๋ค ๋ก์ง์ ๊ธฐ์ค์ผ๋ก ํต์ฌ์ ์ธ ๊ด์ , ๋ถ๊ฐ์ ์ธ ๊ด์ ์ผ๋ก ๋๋์ด์ ๋ณด๊ณ ๊ทธ ๊ด์ ์ ๊ธฐ์ค์ผ๋ก ๊ฐ๊ฐ ๋ชจ๋ํํ๊ฒ ๋ค๋ ์๋ฏธ์ด๋ค.
- ํต์ฌ ๊ด์ฌ์ฌ : ์ฐ๋ฆฌ๊ฐ ์ ์ฉํ๊ณ ์ ํ๋ ํต์ฌ ๋น์ฆ๋์ค ๋ก์ง
- ํก๋จ ๊ด์ฌ์ฌ : ์ฌ๋ฌ ๋ชจ๋์ ๊ณตํต์ ์ผ๋ก ๋ํ๋๋ ๋ก์ง
์์ ๊ทธ๋ฆผ์ฒ๋ผ ๋ ธ๋ ๋ธ๋ก, ๋นจ๊ฐ ๋ธ๋ก, ํ๋ ๋ธ๋ก์ด ์ฌ๋ฌ ํด๋์ค์์ ์ฌ์ฉ๋๋ค.
์ด๋ ์ฌ๋ฌ ํด๋์ค์์ ์ค๋ณต๋๋ ๋ฉ์๋, ํ๋, ์ฝ๋ ๋ค์ด ๋ํ๋๋ค๋ ๋ป์ด๋ค. ๋ง์ฝ ํด๋์ค A์ ์ฃผํฉ ๋ธ๋ก์ ์์ ํ๋ค๋ฉด ํด๋์ค B,C์ ์ฃผํฉ ๋ธ๋ก๋ ๊ฐ์ด ์์ ํด์ค์ผ ๋ ๊ฒ์ด๋ค. โ ์ ์ง ๋ณด์ํ๊ธฐ ์ข์ง ์์
์ด์ฒ๋ผ ์ฝ๋๋ฅผ ์ง๋ค๋ณด๋ฉด ๋ค๋ฅธ ๋ถ๋ถ์ ๊ณ์ ๋ฐ๋ณตํด์ ์ฐ๋ ์ฝ๋๋ฅผ ๋ณผ ์ ์๋๋ฐ ์ด๋ฅผ โํฉ์ด์ง ๊ด์ฌ์ฌ (Crosscutting Concerns)โ๋ผ๊ณ ๋ถ๋ฅธ๋ค
AOP๋ aspect๋ฅผ ์ฌ์ฉํ์ฌ ํฉ์ด์ง ๊ด์ฌ์ฌ๋ฅผ ํด๊ฒฐํฉ๋๋ค.
โ ์ด๋ฐ ํฉ์ด์ง ๊ด์ฌ์ฌ๋ฅผ Aspect๋ก ๋ชจ๋ํํ๊ณ ํต์ฌ์ ์ธ ๋น์ฆ๋์ค ๋ก์ง์์ ๋ถ๋ฆฌํ์ฌ ์ฌ์ฌ์ฉํ๊ฒ ๋ค๋ ๊ฒ์ด AOP์ ์ทจ์ง์ด๋ค
AOP์ ์ฃผ์ ํค์๋
- Aspect : ์ฌ๋ฌ ๊ณณ์์ ์ฐ์ด๋ ๊ณตํต ๋ถ๋ถ ์ฝ๋๋ฅผ ๋ชจ๋ํํ ๊ฒ
- Target : Aspect๊ฐ ์ ์ฉ๋๋ ๊ณณ
- Advice : Aspect์์ ์ค์ง์ ์ธ ๊ธฐ๋ฅ์ ๋ํ ๊ตฌํ์ฒด
- Joint Point: Advice๊ฐ Target์ ์ ์ฉ๋๋ ์์
- Point Cut : Joint Point์ ์์ธ ์คํ์ ์ ์ํ ๊ฒ
๊ทธ๋ผ ์ด์ ํ๋ฒ ์์ ์ฝ๋๋ฅผ ๋ณด๋ฉฐ ์ดํดํด๋ณด์.
@Service
public class TodoService() {
// todo ์์ฑ
public void createTodo(TodoReq todoReq) {
long begin = System.currentTimeMillis();
try{
todoRepository.save(todoReq);
} finally {
System.out.println(System.currentTimeMillis() - begin);
}
}
// todo ์ญ์
public void deleteTodo(Long id) {
long begin = System.currentTimeMillis();
try{
todoRepository.deleteById(id);
} finally {
System.out.println(System.currentTimeMillis() - begin);
}
}
}์ด๋ ๊ฒ todo๋ฅผ ์์ฑ, ์ญ์ ํ๋ ์ฝ๋์ ์๊ฐ์ ์ธก์ ํ๊ณ ์ถ๋ ฅํ๋ ์ฝ๋๊ฐ ์ค๋ณต์ด ๋๋ค.
์ง๊ธ์ ๋ฉ์๋๊ฐ 2๊ฐ๋ง ์กด์ฌํ๋ ์ํฉ์ด๋ผ ๊ด์ฐฎ์ ๋ณด์ด์ง๋ง ๋ง์ฝ ๋ฉ์๋๊ฐ ์์ฒญ ๋ง๋ค๋ฉด, ์์ฐฝ๋๊ฒ ๋ง์ ์ค๋ณต ์ฝ๋๋ฅผ ์์ฑํ๊ฒ ๋ ๊ฒ์ด๋ค.
โ AOP๋ฅผ ์ฌ์ฉํ์ฌ ์ฝ๋๋ฅผ ๊ฐ์ ํด๋ณด์!
๋ถ๊ฐ ๊ธฐ๋ฅ์ ๋ถ๋ฆฌํ Aspect ์์ฑ
@Aspect
@Component
public class Performance() {
@Around("execution(* com.example.todo.TodoService.*(..))")
public Object countTime(ProceedingJoinPoint joinPoint) throws Throwable {
// ๋ฉ์๋ ์คํ ์
long begin = System.currentTimeMillis();
// ๋ฉ์๋ ์คํ
Object proceed = joinPoint.proceed();
// ๋ฉ์๋ ์คํ ํ
System.out.println(System.currentTimeMillis() - begin);
return proceed;
}
}@Aspect์ด๋ ธํ ์ด์ ์ ์ฌ์ฉํ์ฌ ํด๋น ํด๋์ค๊ฐ AOP์ Aspect์์ ๋ํ๋ธ๋ค. โ@Component์ด๋ ธํ ์ด์ ์ผ๋ก Aspect ํด๋์ค๋ฅผ ๋น์ผ๋ก ๋ฑ๋กํด ์คํ๋ง ์ปจํ ์ด๋๋ก๋ถํฐ ์์กด์ฑ์ ์ฃผ์ ๋ฐ์ ์ฌ์ฉํ๋ค (AOP์ IoC์ ๊ด๊ณ)@Around์ด๋ ธํ ์ด์ ์ ์ฌ์ฉํ์ฌ ์ ์ฉ ๋ฒ์๋ฅผ ์ค์ ํ๋ค- execution(* com.example.todo.TodoService.*(..)) : TodoService ํด๋์ค์ ๋ชจ๋ ๋ฉ์๋์ ๋ํด Aspect๋ฅผ ์ ์ฉํ๋ค
- execution(* com.example.todo.TodoService.createTodo(..)) : createTodo ๋ฉ์๋์๋ง Aspect๋ฅผ ์ ์ฉํ๋ค
ProceedingJoinPoint๋ ํธ์ถ๋ ๋ฉ์๋๋ฅผ ๊ฐ์ธ๋ ์ญํ ์ ํ๋ค- proceed()๋ฅผ ํธ์ถํ๋ฉด ์ค์ ๋น์ฆ๋์ค ๋ก์ง์ด ์คํ๋๋ค
์ด๋ ๊ฒ Aspect๋ฅผ ์์ฑํ๋ฉด ๋น์ฆ๋์ค ๋ก์ง๊ณผ ๋ถ๊ฐ ๊ธฐ๋ฅ์ด ๋ถ๋ฆฌ๊ฐ ๋๋ฏ๋ก ์ ์ง ๋ณด์์ฑ์ด ํฅ์๋๊ณ , ์ฌ์ฌ์ฉ์ฑ์ด ์ข์์ง๋ค.
ํ๊ฒฝ์ ๋ณํ์ ๊ด๊ณ์์ด ์ผ๊ด๋ ๋ฐฉ์์ ๊ธฐ์ ๋ก์ ์ ๊ทผ ํ๊ฒฝ์ ์ ๊ณตํ๋ ์ถ์ํ ๊ตฌ์กฐ์ด๋ค.
์ฐ๋ฆฌ๋ JDBC Driver๋ฅผ ์ฌ์ฉํด ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ ๊ทผํ์ง๋ง JDBC Driver๊ฐ ์ด๋ป๊ฒ ๊ตฌํ๋์ด ์๋์ง๋ ๋ชจ๋ฅธ๋ค.
์ด๋ ๊ฒ ์ค์ ๊ตฌํ๋ถ๋ฅผ ๋ชจ๋ฅด๋๋ผ๋ ํด๋น ์๋น์ค๋ฅผ ์ด์ฉํ ์ ์๋๋ก ํ๋ ๊ฒ์ด ์๋น์ค ์ถ์ํ์ด๋ค.
๊ทธ๋ ๋ค๋ฉด Portableํ ์๋น์ค ์ถ์ํ๋ ๋ญ๊น?
PSA๋ ๋น์ฆ๋์ค ๋ก์ง์ ์์ ํ์ง ์๊ณ ์ถ์ํ ๊ณ์ธต์ ๊ตฌํํ ๋ ๋ค๋ฅธ ์๋น์ค๋ก ๊ต์ฒด ๊ฐ๋ฅํ ๊ฒ์ ๋งํ๋ค.
@Controller
public class MemberController {
@GetMapping("/info")
public void getInfo() {
// ...
}
}@Controller
public class MemberController {
@RequestMapping(value="/info", method=RequestMethod.GET)
public void getInfo() {
// ...
}
}์ํ๋ url๋ก ๋งคํํ๊ณ ์ถ์ ๋ @GetMapping, @RequestMapping(method=RequestMethod.GET) ๋ ์ค ์๋ฌด๊ฑฐ๋ ์ฌ์ฉํด๋ ๋๊ฐ์ ๊ธฐ๋ฅ์ ํ๋ค.
๋น์ฆ๋์ค ๋ก์ง์ ์์ ํ์ง ์๊ณ , http url ๋งคํ์ด ์ถ์ํ ๋์ด์๋ ๋ ๋ค๋ฅธ ์๋น์ค๋ก ๊ต์ฒด ๊ฐ๋ฅํ๊ธฐ ๋๋ฌธ์ PSA๋ผ๊ณ ํ ์ ์๋ค. (@GetMapping์์ @RequestMapping์ผ๋ก)
Spring Bean ๐ซ
: ์คํ๋ง IoC ์ปจํ ์ด๋๊ฐ ๊ด๋ฆฌํ๋ ์๋ฐ ๊ฐ์ฒด๋ฅผ ๋งํ๋ค
๋น์ด ์์ฑ๋๊ณ ์ฌ์ฉ๋ ํ ์๋ฉธ๋๋ ์ ์ฒด ๊ณผ์ ์ ์๋ฏธํ๋ค
๊ฐ๋จํ๊ฒ ๋งํ๋ฉด โ๊ฐ์ฒด ์์ฑ โ ์์กด๊ด๊ณ ์ฃผ์ โ ์ ๋ผ์ดํ ์ฌ์ดํด์ ๊ฐ์ง๋ค.
1) ์คํ๋ง ์ปจํ ์ด๋ ์์ฑ
// ๊ตฌ์ฑ, ์ค์ ์ ๋ณด
@Configuration
public class AppConfig {
// ...
}
public class SpringContainerExample {
public static void main(Stirng[] args) {
// ApplicationContext(์คํ๋ง ์ปจํ
์ด๋) ์์ฑ
ApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
}
}
์คํ๋ง ์ปจํ ์ด๋์ธ ApplicationContext๊ฐ ์คํ๋ง ๋น์ ๊ด๋ฆฌํ๋ค.
AnnotationConfigApplicationContext() ์์ฑ์๋ฅผ ํธ์ถํด ์คํ๋ง ์ปจํ ์ด๋๋ฅผ ์์ฑํ๋ค. ์ด๋, ๊ตฌ์ฑ์ ๋ณด ํด๋์ค์ธ AppConfig.class๋ฅผ ํ๋ผ๋ฏธํฐ๋ก ์ ๋ฌํด์ค๋ค (โ ์คํ๋ง๋ถํธ ์์ด ์คํ๋ง ์ปจํ ์ด๋๋ฅผ ์์ฑํ ๋ ์ง์ config๋ฅผ ํ๋ผ๋ฏธํฐ๋ก ์ ๋ฌํ๋ค๊ณ ํ๋ค)
2) ๋น ๋ฑ๋ก
// AppConfig.class
@Bean
public MemberService memberService() {
return new MemberServiceImpl(memberRepository());
}
@Bean
public MemberRepository memberRepository() {
return new MemoryMemberRepository();
}
์ ๋ฌ๋ฐ์ AppConfig๋ฅผ ์ฌ์ฉํด์ ์คํ๋ง ๋น์ ๋ฑ๋กํด์ค๋๋ค.
๊ธฐ๋ณธ์ ์ผ๋ก ๋ฉ์๋ ์ด๋ฆ์ด ๋น์ ์ด๋ฆ์ผ๋ก ์ค์ ๋ฉ๋๋ค.
3) ์์กด์ฑ ์ฃผ์
AppConfig ๋ด์ฉ์ ์ฐธ๊ณ ํด ์์กด๊ด๊ณ๋ฅผ ์ฃผ์ ํฉ๋๋ค. ์ค์ ๋ก๋ ์คํ๋ง ๋น์ ๋ฑ๋กํ๋ฉด, ์์ฑ์๋ฅผ ํธ์ถํ์ฌ ์์กด๊ด๊ณ ์ฃผ์ ์ด ์ผ์ด๋๋ค๊ณ ํฉ๋๋ค.
AppConfig๋ฅผ ๋ณด๋ฉด memberService ๋ฉ์๋์์ memberRepository๋ฅผ ์ฌ์ฉํ๋๊ฑธ ๋ณผ ์ ์์ต๋๋ค. ์ด ๋ง์ โmemberService๊ฐ memberRepository๋ฅผ ์์กดํ๋คโ๋ผ๊ณ ์ดํดํ ์ ์์ต๋๋ค.
4) ์ด๊ธฐํ
์์กด๊ด๊ณ ์ฃผ์ ์ด ์๋ฃ๋๋ฉด, ์คํ๋ง์ ์คํ๋ง ๋น์๊ฒ ์ฝ๋ฐฑ ๋ฉ์๋๋ฅผ ํตํด ์ด๊ธฐํ ์์ ์ ์๋ ค์ค๋ค.
์ฌ๊ธฐ์ ๋งํ๋ ์ด๊ธฐํ ์์ ์ด๋? ์ฃผ๋ก ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฐ๊ฒฐ, ๋ฆฌ์์ค ์ด๊ธฐํ ๋ฑ ๊ฐ์ฒด๊ฐ ์ฌ์ฉ๋๊ธฐ ์ ์ ํ์ํ ์์ ๋ฑ์ ์๋ฏธํ๋ค.
@PostConstruct
public void init() {
System.out.println("๋น ์ด๊ธฐํ");
// .. ์ด๊ธฐํ ๋ก์ง ..
}@PostConstruct ์ด๋
ธํ
์ด์
์ ์ฌ์ฉํ๋ฉด ์์กด๊ด๊ณ ์ฃผ์
ํ ํด๋น ๋ฉ์๋๋ฅผ ํธ์ถํด์ค๋ค.
5) ๋น ์ฌ์ฉ
๋น์ ์ด๊ธฐํ๊น์ง ์๋ฃ๋๋ฉด, ์ ํ๋ฆฌ์ผ์ด์ ์ ๋น์ ์ฌ์ฉํฉ๋๋ค.
๋น์ ์์ฒญ์ ์ฒ๋ฆฌํ๊ฑฐ๋ ๋ค๋ฅธ ์๋น์ค์ ์ํธ์์ฉ ํ ์ ์์ต๋๋ค.
6) ์๋ฉธ
์คํ๋ง์ ์คํ๋ง ์ปจํ ์ด๋๊ฐ ์ข ๋ฃ๋๊ธฐ ์ง์ ์ ์๋ฉธ ์ฝ๋ฐฑ์ ์ค๋ค.
์ ํ๋ฆฌ์ผ์ด์ ์ด ์ข ๋ฃ๋๊ฑฐ๋, ๋น์ด ๋ ์ด์ ํ์ ์์๋ ์๋ฉธ๋ฉ๋๋ค.
@PreDestroy
public void destroy() {
System.out.println("๋น ์๋ฉธ");
// .. ์๋ฉธ ๋ก์ง..
}@PreDestroy ์ด๋
ธํ
์ด์
์ ์ฌ์ฉํ๋ฉด ์คํ๋ง ์ปจํ
์ด๋๊ฐ ์ข
๋ฃ๋๊ธฐ ์ง์ ์ ํด๋น ๋ฉ์๋๋ฅผ ํธ์ถํด์ค๋ค.
7) ์ปจํ ์ด๋ ์ข ๋ฃ
์ ํ๋ฆฌ์ผ์ด์ ์ด ์์ ํ ์ข ๋ฃ๋๋ฉด, ์คํ๋ง ์ปจํ ์ด๋๋ ์์ ์ด ๊ด๋ฆฌํ๋ ๋ชจ๋ ๋น์ ์๋ฉธ์ํค๊ณ ๋ฆฌ์์ค๋ฅผ ํด์ ํฉ๋๋ค.
์๋ฐ์์ ์ฝ๋ ์ฌ์ด์ ํน๋ณํ ์๋ฏธ, ๊ธฐ๋ฅ์ ์ํํ๋๋ก ํ๋ ๊ธฐ์ ์ ๋งํ๋ค.
ํด๋์ค, ๋ฉ์๋, ํ๋ ๋ฑ์ ์์์ ๋ฉํ๋ฐ์ดํฐ๋ฅผ ์ถ๊ฐํ์ฌ ๋์ ๋ฐฉ์์ ์ ์ํ๋ค. ์ด๋ ธํ ์ด์ ์ ํตํด ์คํ๋ง์ ๋ค์ํ ๊ธฐ๋ฅ์ ์๋์ผ๋ก ์ฒ๋ฆฌํ ์ ์๋ค.
์ด๋ ธํ ์ด์ ๋ง๋ค์ด๋ณด๊ธฐ
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface Login {
// ..๊ตฌํ ๋ด์ฉ
}@Target- ์ด๋ ธํ ์ด์ ์ด ์ ์ฉ๋ ์ ์๋ ์์์ ํ์ ์ ์ ์ํ๋ค.
- ElementType.PARAMETER ๋ฅผ ์ฌ์ฉํ๋ฉด ๋ฉ์๋ ํ๋ผ๋ฏธํฐ์์ ํด๋น ์ด๋ ธํ ์ด์ ์ ์ฌ์ฉํ ์ ์์์ ์๋ฏธํ๋ค
- ElementType.TYPE : ํด๋์ค, ์ธํฐํ์ด์ค, ์ด๊ฑฐํ, ์ด๋ ธํ ์ด์ ํ์ ์ ์ ์ฉ๋๋ค
- ElementType.METHOD : ๋ฉ์๋์ ์ ์ฉ๋๋ค
- ElementType.FIELD : ํด๋์ค์ ํ๋์ ์ ์ฉ๋๋ค
@Retention- ์ด๋ ธํ ์ด์ ์ ์ ์ง ๊ธฐ๊ฐ์ ์ ์ํ๋ค.
- RetentionPolicy.RUNTIME ๋ ์ด๋ ธํ ์ด์ ์ด runtime์๋ ์ ์ง ๋จ์ ์๋ฏธํ๋ค โ ์ด ์ด๋ ธํ ์ด์ ์ ์คํ ์ค์๋ ์ฌ์ฉํ ์ ์์ผ๋ฉด reflection์ ํตํด ์ ๊ทผ ํ ์ ์๋ค.
- RetentionPolicy.SOURCE : ์ปดํ์ผ ์์ ์๋ง ์ ์ง
- RetentionPolicy.CLASS : ์ปดํ์ผ ํ, ํด๋์ค ํ์ผ์ ํฌํจ๋์ง๋ง ๋ฐํ์์๋ ์ ์ง๋์ง ์์
@interface- ์๋ฐ์์ ์ด๋ ธํ ์ด์ ์ ์ ์ํ ๋ ์ฌ์ฉํ๋ค.
@RestController
public class MyController {
@GetMapping("/user")
public String getUser(@Login String userId) {
// .. ๊ตฌํ ๋ด์ฉ
return (userId);
}
}ํ์ฌ @Login ์ด๋ผ๋ ์ฌ์ฉ์ ์ ์ ์ด๋ ธํ ์ด์ ์ ์ ์ํ๊ณ , ํ๋ผ๋ฏธํฐ์์ ์ฌ์ฉํ ์ ์๋ค.
userId ๋ผ๋ ํ๋ผ๋ฏธํฐ์ ํน์ ์ฒ๋ฆฌ๋ฅผ ์ํํ ์ ์๋ค.
์คํ๋ง ์ปจํ ์ด๋์๊ฒ ์ ์ด๊ถ์ ๋๊ธฐ๋ ค๋ฉด ๊ฐ์ฒด๋ฅผ bean ์ผ๋ก ๋ฑ๋กํด์ผ ํ๋ค.
์์์ ๋งํ๋ฏ์ด ์ด๋ ธํ ์ด์ ์ ์ฌ์ฉํด ๋น์ ๋ฑ๋กํ ์ ์๋๋ฐ, ๋ ์์ธํ๊ฒ ์์๋ณด์!
1) @Configuration + @Bean
์ด ๋ฐฉ์์ ์ฃผ๋ก ๊ฐ๋ฐ์๊ฐ ์ง์ ์ ์ด ๋ถ๊ฐ๋ฅํ ์ธ๋ถ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ๋ฑ์ bean์ผ๋ก ๋ฑ๋กํ ๋ ์ฌ์ฉํ๋ค.
@Configuration
public class AppConfig {
@Bean
public ArrayList<String> array() {
return new ArrayList<String>();
}
}์คํ๋ง ์ปจํ ์ด๋๋ @Configuration ์ด๋ ธํ ์ด์ ์ด ๋ถ์ ํด๋์ค๋ฅผ ์๋์ผ๋ก ๋น์ผ๋ก ๋ฑ๋กํด์ค๋ค.
@Bean์ด ๋ถ์ ๋ฉ์๋๋ ์๋ฐ ๊ฐ์ฒด๋ฅผ ๋ฐํํ๊ณ , ๋ฐํ๋ ์๋ฐ ๊ฐ์ฒด๋ ์คํ๋ง ์ปจํ ์ด๋์ ๋น์ผ๋ก ๋ฑ๋ก๋๋ค.
์์ ์์๋ ArrayList์ ๊ฐ์ ์ธ๋ถ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ bean์ผ๋ก ๋ฑ๋กํ๊ธฐ ์ํด ArrayList๋ฅผ ๋ฐํํ๋ ๋ฉ์๋๋ฅผ ๋ง๋ค๊ณ @Bean ์ด๋ ธํ ์ด์ ์ ์ถ๊ฐํด์ค ๋ฐํ๋ ๊ฐ์ฒด๊ฐ ๋น์ผ๋ก ๋ฑ๋ก๋ ์ ์๊ฒ ํ์๋ค.
โ ์ธ๋ถ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ ์ง์ ์์ ํ ์ ์๊ธฐ ๋๋ฌธ์ ์์ ๊ฐ์ ๋ฐฉ๋ฒ์ ์ฌ์ฉํ๋ ๊ฒ์ด๋ค.
2) @Component
๊ฐ๋ฐ์๊ฐ ์ง์ ์์ฑํ class๋ฅผ bean์ผ๋ก ๋ฑ๋ก์ํค๊ธฐ ์ํ ๋ฐฉ๋ฒ์ด๋ค.
@Component๋ ํด๋น ํด๋์ค๋ฅผ ๋น์ผ๋ก ๋ฑ๋ก์ํค๊ฒ ๋ค๋ ์๋ฏธ์ด๋ค.
@Component
public class Car {
private final Tire tire;
@Autowired
public Car(Tire tire) {
this.tire = tire;
}
}@Autowired ์ด๋
ธํ
์ด์
์ ์ฌ์ฉํ๋ฉด ์คํ๋ง์๊ฒ ์๋์ผ๋ก ์์กด์ฑ์ ์ฃผ์
ํ๋ผ๊ณ ์ง์ํ๋ค.
์คํ๋ง ์ปจํ ์ด๋๋ Car ๋น์ ์์ฑํ ๋, Tire ํ์ ์ ๋น์ ์ฐพ์์ ์์ฑ์์ ์ฃผ์ ํ๋ค.
๐จย ๊ทธ๋ผ ์คํ๋ง ์ปจํ ์ด๋๋ Car ํด๋์ค๊ฐ ์ด๋์๋์ง ์๊ณ ์ฐพ์์ ๋น์ ์์ฑํด์ฃผ๋ ๊ฒ์ผ๊น??
์ด ์ด๋ ธํ ์ด์ ์ @Component ์ด๋ ธํ ์ด์ ์ด ๋ถ์ ํด๋์ค๋ค์ ์๋์ผ๋ก bean ์ผ๋ก ๋ฑ๋กํด์ฃผ๋ ์ญํ ์ ํ๋ค
๊ทธ๋ผ ์ด๋๋ถํฐ @Component ํ์์ ์์ํ๋์?? โ @ComponentScan ์ด ๋ถ์ ํด๋์ค์ ํจํค์ง ๊ฒฝ๋ก๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ํ์์ ์์ํ์ฌ, @Component ๊ฐ ๋ถ์ ํด๋์ค๋ฅผ ์ฐพ์ผ๋ฉด ๋น์ผ๋ก ๋ฑ๋ก์์ผ์ค๋ค.
๊ทธ๋ฐ๋ฐ ๊ธฐ์กด์ ํ๋ก์ ํธ๋ฅผ ํ ๋ @ComponentScan ์ด๋ ธํ ์ด์ ์ ์ด ๊ธฐ์ต์ด ์๋๋ฐ์??
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}ํ๋ก์ ํธ์ root์ Application ํด๋์ค๊ฐ ์๋๋ฐ, ์ด ํด๋์ค์ @SpringBootApplication ์ด๋
ธํ
์ด์
๋ด๋ถ๋ฅผ ์ดํด๋ณด๋ฉด ์๋์ ๊ฐ์ด ๊ตฌํ๋์ด ์๋ค.
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
// ๊ตฌํ
}๋ด๋ถ์ @ComponentScan ์ด๋ ธํ ์ด์ ์ด ์๋ ๊ฒ์ ๋ณผ ์ ์๋ค.
ํ๋ก์ ํธ root์ @SpringBootApplication ์ด ์๊ณ , ๊ทธ ๋ด๋ถ์ @ComponentScan์ด ์์ด ํญ์ ํ๋ก์ ํธ root๋ถํฐ ํ์ ํจํค์ง๊น์ง @Component๋ฅผ ํ์ ํ ์ ์์๋ ๊ฒ์ด๋ค.
์ฐ๋ฆฌ๊ฐ ์์ฃผ ์ฌ์ฉํ๋ ์ด๋ ธํ ์ด์ ์ ์ดํด๋ณด์
@Controller
@Repository
@Service
@Configuration
๋ชจ๋ ๋ด๋ถ์ @Component ์ด๋ ธํ ์ด์ ์ ๊ฐ์ง๊ณ ์๋ค. ๋ฐ๋ผ์ ๋ค๋ค ์ปดํฌ๋ํธ ์ค์บ์ ๋์์ด ๋์ด ์๋ ๋น ๋ฑ๋ก์ด ๋์๋ ๊ฒ์ด๋ค.
์ ํ๋ฆฌ์ผ์ด์ ์ ๊ฐ์ฅ ์์ ๋จ์์ธ ํด๋์ค๋ ๋ฉ์๋์ ๋์์ ํ ์คํธํ๋ ๊ฒ์ ๋งํ๋ค.
์คํ๋ง์์ ์ฃผ๋ก Mockito๋ JUnit๊ณผ ๊ฐ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ์ฌ ๋จ์ ํ ์คํธ๋ฅผ ์์ฑํ๋ค.
๋จ์ ํ ์คํธ์ ์ฅ์
- ์์ ๋ถ๋ถ์ ๋ ๋ฆฝ์ ์ผ๋ก ํ ์คํธ ํ๋ฏ๋ก ํ ์คํธํ๋ ์๊ฐ๊ณผ ๋น์ฉ์ ์ ๊ฐํ ์ ์๋ค.
- ์๋ก์ด ๊ธฐ๋ฅ ์ถ๊ฐ ๋๋ ๋ณ๊ฒฝ ์ฌํญ์ด ์๊ฒผ์ ๋ ๋น ๋ฅด๊ฒ ํ ์คํธ ํ ์ ์๋ค.
- ๋ฆฌํฉํ ๋ง ์์ ์์ ์ฑ์ ํ๋ณดํ ์ ์๋ค.
๊ฐ๋จํ ์๋ฅผ ํตํด ๋จ์ ํ ์คํธ ์์ฑ๋ฒ์ ์์๋ณด์
// .. ์๋น์ค ํด๋์ค
public class CalculateService {
public int add(int a, int b) {
return (a + b);
}
}// .. ๋จ์ ํ
์คํธ
public class CalculateServiceTest {
private CalculateService calculateService;
@BeforeEach
public void setUp() {
// ํ
์คํธ ๋์ ํด๋์ค์ ์ธ์คํด์ค๋ฅผ ์์ฑํ๋ค.
calculateService = new CalculateService();
}
@Test
public void testAdd() {
// given
int a = 5;
int b = 3;
// when
int result = calculateService.add(a, b);
// then
assertEquals(8, result, "5 + 3 = 8");
}
}@BeforeEach- ๊ฐ ํ ์คํธ ๋ฉ์๋๊ฐ ์คํ๋๊ธฐ ์ ์ ์คํ๋ ๋ฉ์๋๋ฅผ ์ ์ํ๋ค.
- CalculateService ์ธ์คํด์ค๋ฅผ ์ด๊ธฐํ ์ํจ๋ค. ๋ฐ๋ผ์ ๋ชจ๋ ๋ฉ์๋๊ฐ ๋ ๋ฆฝ์ ์ผ๋ก ์คํ๋๋ฉด, ๊ฐ๊ฐ์ ํ ์ค๋ ์๋ก์ด ์ธ์คํด์ค๋ฅผ ์ฌ์ฉํ๋ค.
@Test- JUnit์์ ์ ๊ณตํ๋ ์ด๋ ธํ ์ด์ ์ด๋ฉฐ, ์ด ๋ฉ์๋๊ฐ ํ ์คํธ ๋ฉ์๋์์ ๋ํ๋ธ๋ค.
- ๐ย ํ
์คํธ ๋ฉ์๋ ์์ฑ๋ฒ
- given : ํ ์คํธ์์ ํ์ํ ์ ๋ ฅ๊ฐ์ ์ค๋นํ๋ ๋จ๊ณ
- when : ํ ์คํธ ๋์ ๋ฉ์๋๋ฅผ ํธ์ถํ๋ ๋จ๊ณ
- then : ๊ฒฐ๊ณผ๋ฅผ ๊ฒ์ฆํ๋ ๋จ๊ณ
- ๐ย ๋จ์ ํ
์คํธ์์ ์์ฃผ ์ฌ์ฉํ๋ assert ๋ฉ์๋
- assertEquals(๊ธฐ๋ ๊ฐ, ์ค์ ๊ฐ) : ๊ธฐ๋ ๊ฐ๊ณผ ์ค์ ๊ฐ์ด ๊ฐ์์ง ๊ฒ์ฆํ๋ค.
- assertTrue(์กฐ๊ฑด) : ์กฐ๊ฑด์ด ์ฐธ์ธ์ง ๊ฒ์ฆํ๋ค.
- assertFalse(์กฐ๊ฑด) : ์กฐ๊ฑด์ด ๊ฑฐ์ง์ธ์ง ๊ฒ์ฆํ๋ค.
- assertNotNull(๊ฐ์ฒด) : ๊ฐ์ฒด๊ฐ null์ด ์๋์ง ๊ฒ์ฆํ๋ค.
๊ทธ๋ผ ์ธ๋ถ ์์กด์ฑ์ ๊ฐ์ง ํด๋์ค์ ๋จ์ ํ ์คํธ๋ฅผ ์์ฑํ ๋๋ ์ด๋ป๊ฒ ํด์ผ๋ ๊น?
โ ์์กด์ฑ์ ๋ชจํน(Mock) ํ์ฌ ํ ์คํธํ์
// .. ์๋น์ค ํด๋์ค
public class UserService {
private final UserRepository UserRepository;
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
// .. UserRepository๋ฅผ ์์กดํ๋ findUserById ๋ฉ์๋
public User findUserById(Long id) {
return UserRepository.findById(id).orElseThrow(() -> new RuntimeException("ํ์ ์์"));
}
}// .. ๋จ์ ํ
์คํธ
public class UserServiceTest {
@Mock
private UserRepository UserRepository;
@InjectMocks
private UserService userService;
@BeforeEach
public void setUp() {
MockitoAnnotations.openMocks(this);
}
@Test
public void testFindUserById() {
// given
User mockUser = new User(1L, "seoji");
when(userRepository.findById(1L)).thenReturn(Optional.of(mockUser));
// when
User user = userService.findUserById(1L);
// then
assertEquals("seoji", user.getName());
verify(userRepository).findById(1L);
}
}@Mock- UserRepository๋ฅผ ๋ชจํนํ๋ค.
- ์ค์ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ํต์ ํ๋ ๊ฒ์ด ์๋๊ณ ๋ชจํน๋ repository ๋ฅผ ์ฌ์ฉํ๋ค.
@InjectMocks- UserService์ ์ธ์คํด์ค๋ฅผ ์์ฑํ๋ฉด ์ฝ๋ฉ๋ UserRepository๋ฅผ ์ฃผ์ ํ๋ค.
- when(userRepository.findById(1L)).thenReturn(Optional.of(mockUser))
- ์ฐ๋ฆฌ๋ ์ง๊ธ ์๋น์ค ํด๋์ค๋ฅผ ํ ์คํธํ๋ ๊ฒ์ด๋ฏ๋ก ๋ชจํน๋ repository๊ฐ ์ด๋ค ๊ฐ์ ๋ฐํํ ์ง๋ฅผ ์ ํด์ค์ผ ํ๋ค.
- โ ๐ย ์๋น์ค ํด๋์ค ์์ฒด์ ๋ก์ง๋ง์ ๋ ๋ฆฝ์ ์ผ๋ก ํ ์คํธํ๊ธฐ ์ํด์
- userRepository.findById(1L)์ด ํธ์ถ๋ ๋, Optional.of(mockUser)์ด ๋ฐํ๋๋๋ก ํ๋ค.
- verify(userRepository).findById(1L)
- ํ ์คํธ๊ฐ ๋๋ ํ, userRepository.findById(1L)๊ฐ ์ค์ ๋ก ํธ์ถ ๋์๋์ง ํ์ธํ๋ค.
์ ํ๋ฆฌ์ผ์ด์ ์ ์ฌ๋ฌ ๊ตฌ์ฑ ์์๋ ๋ชจ๋์ด ์ค์ ๋ก ์ ๋์ํ๋์ง ํ์ธํ๊ธฐ ์ํ ํ ์คํธ์ด๋ค.
์๋ก ๋ค๋ฅธ ๋ชจ๋ (์๋น์ค, ๋ฐ์ดํฐ๋ฒ ์ด์ค ๋ฑ)์ด ํตํฉ๋์ด ์ํํ๊ฒ ์ํธ์์ฉ ํ๊ณ ์๋์ง ํ์ธํ๋ค.
๊ฐ๋จํ ์์๋ก ํตํฉ ํ ์คํธ ์์ฑ๋ฒ์ ์์๋ณด์
@SpringBootTest
@AutoConfigureMockMvc
public class HelloControllerTest {
@Autowired
private MockMvc mvc;
@DisplayName("DisplayName : ํ
์คํธ ์ด๋ฆ์ ์ค์ ํ ์ ์์ต๋๋ค")
@Test
public void getHello() throws Exception {
mvc.perform(MockMvcRequestBuilders.get("/").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(content().string(equalTo("Greetings from Spring Boot!")));
}
}@SpringBootTest- ํตํฉ ํ ์คํธ๋ฅผ ์ํํ๊ธฐ ์ํด ์คํ๋ง ๋ถํธ ์ ํ๋ฆฌ์ผ์ด์ ์ปจํ ์คํธ ์ ์ฒด๋ฅผ ๋ก๋ํ๋ค.
- ๋ชจ๋ ๋น์ ์ปจํ ์ด๋์ ์ฌ๋ฆฌ๊ณ ์์ํ๋ฏ๋ก ์ด์ํ๊ฒฝ๊ณผ ์ ์ฌํ ํ๊ฒฝ์์ ํ ์คํธ ํ ์ ์๋ค. โ ํ์ง๋ง ์๊ฐ์ด ์ค๋ ๊ฑธ๋ฆผ
- ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ชจ๋ bean๊ณผ ์ค์ ์ด ๋ก๋๋๋ฉฐ, ์ด๋ฅผ ํตํด ์ฌ๋ฌ ๊ณ์ธต (Controller, Service, Repository๋ฑ)์ ํตํฉํ์ฌ ํ ์คํธ ํ ์ ์๋ค.
@AutoConfigureMockMvc- MockMvc๋ฅผ ์๋์ผ๋ก ๊ตฌ์ฑํ์ฌ ์คํ๋ง MVC์ ๋์์ ์๋ฎฌ๋ ์ด์ ํ์ฌ ์ปจํธ๋กค๋ฌ ๊ณ์ธต์ ํ ์คํธ ํ ์ ์๊ฒ ํด์ค๋ค.
- MockMvc
- HTTP ์์ฒญ์ ๋ชจํนํ์ฌ ์ปจํธ๋กค๋ฌ์ ๋ํ ํ ์คํธ๋ฅผ ์ํํ ์ ์๊ฒ ๋์์ค๋ค.
- โHTTP ์์ฒญ์ ๋ชจํน(Mock)ํ๋คโ๋ ๊ฒ์ ์ค์ ๋คํธ์ํฌ๋ฅผ ํตํด ์์ฒญ์ ๋ณด๋ด์ง ์๊ณ , ํ๋ก๊ทธ๋จ ๋ด๋ถ์์ ๊ฐ์์ ์์ฒญ์ ์์ฑํ์ฌ ์์ฒญํ๋ ๊ฒ์ ๋งํ๋ค.
- mvc.perform(MockMvcRequestBuilders.get(โ/โ))
- MockMvc๋ฅผ ์ฌ์ฉํ์ฌ HTTP GET ์์ฒญ์ โ/โ ๊ฒฝ๋ก๋ก ๋ณด๋ธ๋ค.
- ์์ฒญ์ HelloController์ โ/โ ๊ฒฝ๋ก๋ฅผ ์ฒ๋ฆฌํ๋ ๋ฉ์๋๋ก ์ ๋ฌ๋๋ค.
- andExpect(status().isOk())
- HTTP ์๋ต ์ํ๊ฐ 200 OK ์ธ์ง ํ์ธํ๋ค
- andExpect(content().string(equalTo("Greetings from Spring Boot!")))
- ์ปจํธ๋กค๋ฌ์ ๋ฉ์๋์์ ๋ฐํ๋๋ ๊ฒฐ๊ณผ๋ฅผ ๊ฒ์ฆํ๋ ์ฝ๋์ด๋ค.
- ์๋ต ๋ณธ๋ฌธ ๋ด์ฉ์ด "Greetings from Spring Boot!"์ ์ผ์นํ๋์ง ํ์ธํ๋ค.
๐ย ์ค์ ์ฝ๋๊ฐ ๋ณ๊ฒฝ๋๋ฉด ํ ์คํธ ์ฝ๋๋ ๋ณ๊ฒฝ์ด ํ์ํ ์ ์๋ค. ํ ์คํธ ์ฝ๋๋ ๊ฐ๋ ์ฑ ์๊ฒ ์ ์์ฑํ๋๋ก ํ์!