ํ์ฅ ๊ฐ๋ฅํ๊ณ ์ ์ง๋ณด์ํ๊ธฐ ์ฌ์ฐ๋ฉฐ ๊ฒฌ๊ณ ํ ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ตฌ์ถํ๊ธฐ ์ํ ๊ณ ๊ธ ์คํ๋ง ๊ฐ๋ฐ ๊ธฐ์ ์ ํ์ํด ๋ณด์ธ์. ๋ชจ๋ฒ ์ฌ๋ก์ ์ค์ฉ์ ์ธ ํ์ ๋ฐฐ์๋ณด์ธ์.
์คํ๋ง ๊ฐ๋ฐ ๋ง์คํฐํ๊ธฐ: ๊ฒฌ๊ณ ํ ์ ํ๋ฆฌ์ผ์ด์ ๊ตฌ์ถ์ ์ํ ๊ธฐ์
์คํ๋ง ํ๋ ์์ํฌ๋ ์๋ฐ ์ํฐํ๋ผ์ด์ฆ ๊ฐ๋ฐ์ ์ด์์ด ๋์์ผ๋ฉฐ, ๊ฐ๋จํ ์น ์ฑ๋ถํฐ ๋ณต์กํ ๋ง์ดํฌ๋ก์๋น์ค ์ํคํ ์ฒ์ ์ด๋ฅด๊ธฐ๊น์ง ๊ด๋ฒ์ํ ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ตฌ์ถํ๊ธฐ ์ํ ํฌ๊ด์ ์ธ ์ธํ๋ผ๋ฅผ ์ ๊ณตํฉ๋๋ค. ์ด ๊ฐ์ด๋์์๋ ํ์ฅ ๊ฐ๋ฅํ๊ณ ์ ์ง๋ณด์ํ๊ธฐ ์ฌ์ฐ๋ฉฐ ๊ฒฌ๊ณ ํ ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ตฌ์ถํ๊ธฐ ์ํ ์ค์ฉ์ ์ธ ์กฐ์ธ๊ณผ ๋ชจ๋ฒ ์ฌ๋ก๋ฅผ ์ ๊ณตํ๋ฉฐ, ๊ณ ๊ธ ์คํ๋ง ๊ฐ๋ฐ ๊ธฐ์ ์ ๋ํด ์์ธํ ์์๋ด ๋๋ค.
ํต์ฌ ์์น ์ดํดํ๊ธฐ
๊ณ ๊ธ ๊ธฐ์ ์ ์ดํด๋ณด๊ธฐ ์ ์ ์คํ๋ง์ ํต์ฌ ์์น์ ํ์คํ ์ดํดํ๋ ๊ฒ์ด ์ค์ํฉ๋๋ค:
- ์์กด์ฑ ์ฃผ์ (DI): ์ด ๋์์ธ ํจํด์ ์ปดํฌ๋ํธ ๊ฐ์ ๊ฒฐํฉ์ ๋ฎ์ถฐ ์ฝ๋๋ฅผ ๋ ๋ชจ๋ํํ๊ณ ํ ์คํธํ๊ธฐ ์ฝ๊ฒ ๋ง๋ญ๋๋ค. ์คํ๋ง์ DI ์ปจํ ์ด๋๋ ๋น(bean) ๊ฐ์ ์์กด์ฑ์ ๊ด๋ฆฌํ๊ณ ๋ฐํ์์ ์ฃผ์ ํฉ๋๋ค.
- ์ ์ด์ ์ญ์ (IoC): IoC๋ ๊ฐ์ฒด ์์ฑ ๋ฐ ์์กด์ฑ ๊ด๋ฆฌ์ ์ ์ด๊ฐ ํ๋ ์์ํฌ๋ก ์ญ์ ๋๋ ๋ ๋์ ๊ฐ๋ ์ ๋๋ค. ์คํ๋ง์ IoC ์ปจํ ์ด๋์ ๋๋ค.
- ๊ด์ ์งํฅ ํ๋ก๊ทธ๋๋ฐ (AOP): AOP๋ฅผ ์ฌ์ฉํ๋ฉด ๋ก๊น , ๋ณด์, ํธ๋์ญ์ ๊ด๋ฆฌ์ ๊ฐ์ ํก๋จ ๊ด์ฌ์ฌ๋ฅผ ๋ชจ๋ํํ ์ ์์ต๋๋ค. ์คํ๋ง AOP๋ฅผ ์ฌ์ฉํ๋ฉด ํต์ฌ ๋น์ฆ๋์ค ๋ก์ง์ ์์ ํ์ง ์๊ณ ๋ ์ด๋ฌํ ๊ด์ฌ์ฌ๋ฅผ ์ ์ฉํ ์ ์์ต๋๋ค.
- ๋ชจ๋ธ-๋ทฐ-์ปจํธ๋กค๋ฌ (MVC): ์คํ๋ง MVC๋ ์น ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ตฌ์ถํ๊ธฐ ์ํ ๊ฒฌ๊ณ ํ ํ๋ ์์ํฌ๋ฅผ ์ ๊ณตํฉ๋๋ค. ๊ด์ฌ์ฌ๋ฅผ ๋ถ๋ฆฌํ์ฌ ์ฝ๋๋ฅผ ๋ ์ฒด๊ณ์ ์ด๊ณ ์ ์ง๋ณด์ํ๊ธฐ ์ฝ๊ฒ ๋ง๋ญ๋๋ค.
๊ณ ๊ธ ์คํ๋ง ๊ฐ๋ฐ ๊ธฐ์
1. ๋น ๋ฅธ ๊ฐ๋ฐ์ ์ํ ์คํ๋ง ๋ถํธ ํ์ฉ
์คํ๋ง ๋ถํธ๋ ์๋ ๊ตฌ์ฑ, ๋ด์ฅ ์๋ฒ ๋ฐ ๊ฐ์ํ๋ ๊ฐ๋ฐ ๊ฒฝํ์ ์ ๊ณตํ์ฌ ๊ฐ๋ฐ ํ๋ก์ธ์ค๋ฅผ ๋จ์ํํฉ๋๋ค. ์คํ๋ง ๋ถํธ๋ฅผ ํจ๊ณผ์ ์ผ๋ก ์ฌ์ฉํ๊ธฐ ์ํ ๋ช ๊ฐ์ง ํ์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค:
- Spring Initializr ์ฌ์ฉ: Spring Initializr (start.spring.io)๋ก ํ๋ก์ ํธ๋ฅผ ์์ํ์ฌ ํ์ํ ์์กด์ฑ์ ๊ฐ์ถ ๊ธฐ๋ณธ ํ๋ก์ ํธ ๊ตฌ์กฐ๋ฅผ ์์ฑํ์ธ์.
- ์๋ ๊ตฌ์ฑ ์ฌ์ฉ์ ์ ์: ์คํ๋ง ๋ถํธ ์๋ ๊ตฌ์ฑ์ด ์๋ํ๋ ๋ฐฉ์์ ์ดํดํ๊ณ ํน์ ์๊ตฌ ์ฌํญ์ ๋ง๊ฒ ์ฌ์ฉ์ ์ ์ํ์ธ์.
application.properties๋๋application.yml์ ์์ฑ์ ์ฌ์ฉํ์ฌ ๊ธฐ๋ณธ ๊ตฌ์ฑ์ ์ฌ์ ์ํ์ธ์. - ์ปค์คํ ์คํํฐ ์์ฑ: ์ฌ์ฌ์ฉ ๊ฐ๋ฅํ ์ปดํฌ๋ํธ๋ ๊ตฌ์ฑ์ด ์๋ ๊ฒฝ์ฐ, ์์ฒด ์คํ๋ง ๋ถํธ ์คํํฐ๋ฅผ ๋ง๋ค์ด ์ฌ๋ฌ ํ๋ก์ ํธ์์ ์์กด์ฑ ๊ด๋ฆฌ ๋ฐ ๊ตฌ์ฑ์ ๋จ์ํํ์ธ์.
- Spring Boot Actuator๋ก ๋ชจ๋ํฐ๋ง: Spring Boot Actuator๋ฅผ ์ฌ์ฉํ์ฌ ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ชจ๋ํฐ๋งํ๊ณ ๊ด๋ฆฌํ์ธ์. ์ํ ํ์ธ, ๋ฉํธ๋ฆญ ๋ฐ ๊ธฐํ ์ ์ฉํ ์ ๋ณด๋ฅผ ์ํ ์๋ํฌ์ธํธ๋ฅผ ์ ๊ณตํฉ๋๋ค.
์์: ์ปค์คํ ์คํ๋ง ๋ถํธ ์คํํฐ ๋ง๋ค๊ธฐ
์ปค์คํ ๋ก๊น ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ์๋ค๊ณ ๊ฐ์ ํด ๋ณด๊ฒ ์ต๋๋ค. ์์กด์ฑ์ผ๋ก ์ถ๊ฐ๋ ๋ ์๋์ผ๋ก ๊ตฌ์ฑ๋๋๋ก ์คํ๋ง ๋ถํธ ์คํํฐ๋ฅผ ๋ง๋ค ์ ์์ต๋๋ค.
- ์คํํฐ๋ฅผ ์ํ ์๋ก์ด Maven ๋๋ Gradle ํ๋ก์ ํธ๋ฅผ ์์ฑํฉ๋๋ค.
- ์ปค์คํ ๋ก๊น ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ํ์ํ ์์กด์ฑ์ ์ถ๊ฐํฉ๋๋ค.
- ๋ก๊น ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ๊ตฌ์ฑํ๋ ์๋ ๊ตฌ์ฑ ํด๋์ค๋ฅผ ์์ฑํฉ๋๋ค.
- ์๋ ๊ตฌ์ฑ์ ํ์ฑํํ๊ธฐ ์ํด
META-INF๋๋ ํ ๋ฆฌ์spring.factoriesํ์ผ์ ์์ฑํฉ๋๋ค. - ์คํํฐ๋ฅผ ํจํค์งํ์ฌ Maven ์ ์ฅ์์ ๋ฐฐํฌํฉ๋๋ค.
2. Spring MVC์ Spring WebFlux๋ก RESTful API ๊ตฌ์ถํ๊ธฐ
Spring MVC์ Spring WebFlux๋ RESTful API๋ฅผ ๊ตฌ์ถํ๊ธฐ ์ํ ๊ฐ๋ ฅํ ๋๊ตฌ๋ฅผ ์ ๊ณตํฉ๋๋ค. Spring MVC๋ ์ ํต์ ์ธ ๋๊ธฐ์ ์ ๊ทผ ๋ฐฉ์์ธ ๋ฐ๋ฉด, Spring WebFlux๋ ๋ฐ์ํ, ๋ ผ๋ธ๋กํน ๋์์ ์ ๊ณตํฉ๋๋ค.
- Spring MVC:
@RestController๋ฐ@RequestMapping์ด๋ ธํ ์ด์ ์ ์ฌ์ฉํ์ฌ API ์๋ํฌ์ธํธ๋ฅผ ์ ์ํฉ๋๋ค. ์คํ๋ง์ ๋ฐ์ดํฐ ๋ฐ์ธ๋ฉ ๋ฐ ์ ํจ์ฑ ๊ฒ์ฌ ๊ธฐ๋ฅ์ ํ์ฉํ์ฌ ์์ฒญ ํ์ด๋ก๋๋ฅผ ์ฒ๋ฆฌํฉ๋๋ค. - Spring WebFlux:
@RestController๋ฐ ๊ธฐ๋ฅ์ ๋ผ์ฐํ ์ ์ฌ์ฉํ์ฌ API ์๋ํฌ์ธํธ๋ฅผ ์ ์ํฉ๋๋ค. Spring WebFlux๋ ๋น๋๊ธฐ ๋ฐ์ดํฐ ์คํธ๋ฆผ์ ์ฒ๋ฆฌํ๊ธฐ ์ํFlux๋ฐMonoํ์ ์ ์ ๊ณตํ๋ ๋ฐ์ํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ธ Reactor๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ๊ตฌ์ถ๋์์ต๋๋ค. ์ด๋ ๋ง์ ์์ ๋์ ์์ฒญ์ ์ฒ๋ฆฌํด์ผ ํ๋ ์ ํ๋ฆฌ์ผ์ด์ ์ ์ ์ฉํฉ๋๋ค. - ์ฝํ
์ธ ํ์(Content Negotiation): ์ฌ๋ฌ ์๋ต ํ์(์: JSON, XML)์ ์ง์ํ๊ธฐ ์ํด ์ฝํ
์ธ ํ์์ ๊ตฌํํฉ๋๋ค. ์์ฒญ์
Acceptํค๋๋ฅผ ์ฌ์ฉํ์ฌ ์ํ๋ ํ์์ ์ง์ ํฉ๋๋ค. - ์ค๋ฅ ์ฒ๋ฆฌ:
@ControllerAdvice๋ฅผ ์ฌ์ฉํ์ฌ ์ ์ญ ์์ธ ์ฒ๋ฆฌ๋ฅผ ๊ตฌํํ์ฌ ์ผ๊ด๋ ์ค๋ฅ ์๋ต์ ์ ๊ณตํฉ๋๋ค.
์์: Spring MVC๋ก RESTful API ๊ตฌ์ถํ๊ธฐ
@RestController
@RequestMapping("/api/products")
public class ProductController {
@Autowired
private ProductService productService;
@GetMapping
public List<Product> getAllProducts() {
return productService.getAllProducts();
}
@GetMapping("/{id}")
public Product getProductById(@PathVariable Long id) {
return productService.getProductById(id);
}
@PostMapping
public Product createProduct(@RequestBody Product product) {
return productService.createProduct(product);
}
@PutMapping("/{id}")
public Product updateProduct(@PathVariable Long id, @RequestBody Product product) {
return productService.updateProduct(id, product);
}
@DeleteMapping("/{id}")
public void deleteProduct(@PathVariable Long id) {
productService.deleteProduct(id);
}
}
์์: Spring WebFlux๋ก ๋ฐ์ํ RESTful API ๊ตฌ์ถํ๊ธฐ
@RestController
@RequestMapping("/api/products")
public class ProductController {
@Autowired
private ProductService productService;
@GetMapping
public Flux<Product> getAllProducts() {
return productService.getAllProducts();
}
@GetMapping("/{id}")
public Mono<Product> getProductById(@PathVariable Long id) {
return productService.getProductById(id);
}
@PostMapping
public Mono<Product> createProduct(@RequestBody Product product) {
return productService.createProduct(product);
}
@PutMapping("/{id}")
public Mono<Product> updateProduct(@PathVariable Long id, @RequestBody Product product) {
return productService.updateProduct(id, product);
}
@DeleteMapping("/{id}")
public Mono<Void> deleteProduct(@PathVariable Long id) {
return productService.deleteProduct(id);
}
}
3. ํก๋จ ๊ด์ฌ์ฌ๋ฅผ ์ํ AOP ๊ตฌํ
AOP๋ฅผ ์ฌ์ฉํ๋ฉด ํก๋จ ๊ด์ฌ์ฌ๋ฅผ ๋ชจ๋ํํ๊ณ ํต์ฌ ๋น์ฆ๋์ค ๋ก์ง์ ์์ ํ์ง ์๊ณ ์ ํ๋ฆฌ์ผ์ด์ ์ ์ ์ฉํ ์ ์์ต๋๋ค. ์คํ๋ง AOP๋ ์ด๋ ธํ ์ด์ ๋๋ XML ๊ตฌ์ฑ์ ์ฌ์ฉํ์ฌ ๊ด์ ์งํฅ ํ๋ก๊ทธ๋๋ฐ์ ์ง์ํฉ๋๋ค.
- Aspect ์ ์:
@Aspect๋ก ์ด๋ ธํ ์ด์ ๋ ํด๋์ค๋ฅผ ๋ง๋ค์ด Aspect๋ฅผ ์ ์ํฉ๋๋ค. - Advice ์ ์:
@Before,@After,@AfterReturning,@AfterThrowing,@Around์ ๊ฐ์ ์ด๋ ธํ ์ด์ ์ ์ฌ์ฉํ์ฌ ๋ฉ์๋ ์คํ ์ , ํ ๋๋ ์ฃผ๋ณ์์ ์คํ๋ Advice๋ฅผ ์ ์ํฉ๋๋ค. - Pointcut ์ ์: Pointcut ํํ์์ ์ฌ์ฉํ์ฌ Advice๊ฐ ์ ์ฉ๋์ด์ผ ํ ์กฐ์ธ ํฌ์ธํธ๋ฅผ ์ง์ ํฉ๋๋ค.
- AOP ํ์ฑํ: ์คํ๋ง ๊ตฌ์ฑ์์
@EnableAspectJAutoProxy๋ฅผ ์ฌ์ฉํ์ฌ AOP๋ฅผ ํ์ฑํํฉ๋๋ค.
์์: AOP๋ก ๋ก๊น ๊ตฌํํ๊ธฐ
@Aspect
@Component
public class LoggingAspect {
private static final Logger logger = LoggerFactory.getLogger(LoggingAspect.class);
@Before("execution(* com.example.service.*.*(..))")
public void logBefore(JoinPoint joinPoint) {
logger.info("Method {} called with arguments {}", joinPoint.getSignature().getName(), Arrays.toString(joinPoint.getArgs()));
}
@AfterReturning(pointcut = "execution(* com.example.service.*.*(..))", returning = "result")
public void logAfterReturning(JoinPoint joinPoint, Object result) {
logger.info("Method {} returned {}", joinPoint.getSignature().getName(), result);
}
@AfterThrowing(pointcut = "execution(* com.example.service.*.*(..))", throwing = "exception")
public void logAfterThrowing(JoinPoint joinPoint, Throwable exception) {
logger.error("Method {} threw exception {}", joinPoint.getSignature().getName(), exception.getMessage());
}
}
4. ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ ๊ทผ์ ์ํ Spring Data JPA ์ฌ์ฉ
Spring Data JPA๋ ์์ฉ๊ตฌ ์ฝ๋๋ฅผ ์ค์ด๋ ๋ฆฌํฌ์งํ ๋ฆฌ ์ถ์ํ๋ฅผ ์ ๊ณตํ์ฌ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ ๊ทผ์ ๋จ์ํํฉ๋๋ค. MySQL, PostgreSQL, Oracle์ ํฌํจํ ๋ค์ํ ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ฅผ ์ง์ํฉ๋๋ค.
- ์ํฐํฐ ์ ์: ๋ฐ์ดํฐ๋ฒ ์ด์ค ํ ์ด๋ธ์ ์๋ฐ ๊ฐ์ฒด์ ๋งคํํ๊ธฐ ์ํด JPA ์ํฐํฐ๋ฅผ ์์ฑํฉ๋๋ค.
- ๋ฆฌํฌ์งํ ๋ฆฌ ์์ฑ: CRUD ์์
์ ์ํํ๊ธฐ ์ํด
JpaRepository๋ฅผ ํ์ฅํ๋ ๋ฆฌํฌ์งํ ๋ฆฌ ์ธํฐํ์ด์ค๋ฅผ ์ ์ํฉ๋๋ค. Spring Data JPA๋ ์ด๋ฌํ ์ธํฐํ์ด์ค์ ๊ตฌํ์ ์๋์ผ๋ก ์์ฑํฉ๋๋ค. - ์ฟผ๋ฆฌ ๋ฉ์๋ ์ฌ์ฉ: ๋ฉ์๋ ์ด๋ฆ ๊ท์น์ด๋
@Query์ด๋ ธํ ์ด์ ์ ์ฌ์ฉํ์ฌ ๋ฆฌํฌ์งํ ๋ฆฌ ์ธํฐํ์ด์ค์ ์ฌ์ฉ์ ์ ์ ์ฟผ๋ฆฌ ๋ฉ์๋๋ฅผ ์ ์ํฉ๋๋ค. - JPA ๋ฆฌํฌ์งํ ๋ฆฌ ํ์ฑํ: ์คํ๋ง ๊ตฌ์ฑ์์
@EnableJpaRepositories๋ฅผ ์ฌ์ฉํ์ฌ JPA ๋ฆฌํฌ์งํ ๋ฆฌ๋ฅผ ํ์ฑํํฉ๋๋ค.
์์: Spring Data JPA ์ฌ์ฉํ๊ธฐ
@Entity
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String description;
private double price;
// Getters and setters
}
public interface ProductRepository extends JpaRepository<Product, Long> {
List<Product> findByName(String name);
List<Product> findByPriceGreaterThan(double price);
}
5. Spring Security๋ก ์ ํ๋ฆฌ์ผ์ด์ ๋ณด์
Spring Security๋ ์ ํ๋ฆฌ์ผ์ด์ ๋ณด์์ ์ํ ํฌ๊ด์ ์ธ ํ๋ ์์ํฌ๋ฅผ ์ ๊ณตํฉ๋๋ค. ์ธ์ฆ, ๊ถํ ๋ถ์ฌ ๋ฐ ๊ธฐํ ๋ณด์ ๊ธฐ๋ฅ์ ์ง์ํฉ๋๋ค.
- ์ธ์ฆ: ์ฌ์ฉ์์ ์ ์์ ํ์ธํ๊ธฐ ์ํด ์ธ์ฆ์ ๊ตฌํํฉ๋๋ค. Spring Security๋ ๊ธฐ๋ณธ ์ธ์ฆ, ํผ ๊ธฐ๋ฐ ์ธ์ฆ, OAuth 2.0 ๋ฑ ๋ค์ํ ์ธ์ฆ ๋ฉ์ปค๋์ฆ์ ์ง์ํฉ๋๋ค.
- ๊ถํ ๋ถ์ฌ: ๋ฆฌ์์ค์ ๋ํ ์ ๊ทผ์ ์ ์ดํ๊ธฐ ์ํด ๊ถํ ๋ถ์ฌ๋ฅผ ๊ตฌํํฉ๋๋ค. ์ญํ ๊ธฐ๋ฐ ์ ๊ทผ ์ ์ด(RBAC) ๋๋ ์์ฑ ๊ธฐ๋ฐ ์ ๊ทผ ์ ์ด(ABAC)๋ฅผ ์ฌ์ฉํ์ฌ ๊ถํ์ ์ ์ํฉ๋๋ค.
- ๋ณด์ ๊ตฌ์ฑ: ์ด๋ ธํ ์ด์ ๋๋ XML ๊ตฌ์ฑ์ ์ฌ์ฉํ์ฌ Spring Security๋ฅผ ๊ตฌ์ฑํฉ๋๋ค. API ์๋ํฌ์ธํธ ๋ฐ ๊ธฐํ ๋ฆฌ์์ค๋ฅผ ๋ณดํธํ๊ธฐ ์ํ ๋ณด์ ๊ท์น์ ์ ์ํฉ๋๋ค.
- JWT ์ฌ์ฉ: RESTful API์์ ์ํ ๋น์ ์ฅ ์ธ์ฆ์ ์ํด JSON ์น ํ ํฐ(JWT)์ ํ์ฉํฉ๋๋ค.
์์: Spring Security ๊ตฌ์ฑํ๊ธฐ
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsService userDetailsService;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/api/public/**").permitAll()
.antMatchers("/api/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
.and()
.httpBasic();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
6. ์คํ๋ง ์ ํ๋ฆฌ์ผ์ด์ ํ ์คํธ
ํ ์คํ ์ ์คํ๋ง ์ ํ๋ฆฌ์ผ์ด์ ์ ํ์ง๊ณผ ์ ๋ขฐ์ฑ์ ๋ณด์ฅํ๋ ๋ฐ ์ค์ํฉ๋๋ค. ์คํ๋ง์ ๋จ์ ํ ์คํธ, ํตํฉ ํ ์คํธ, ์๋ํฌ์๋ ํ ์คํธ๋ฅผ ์ํ ํ๋ฅญํ ์ง์์ ์ ๊ณตํฉ๋๋ค.
- ๋จ์ ํ ์คํธ: JUnit๊ณผ Mockito๋ฅผ ์ฌ์ฉํ์ฌ ๊ฐ๋ณ ์ปดํฌ๋ํธ๋ฅผ ๊ฒฉ๋ฆฌํ์ฌ ํ ์คํธํฉ๋๋ค. ์ธ๋ถ ์์กด์ฑ์ ํผํ๊ธฐ ์ํด ์์กด์ฑ์ ๋ชจ์(mock) ์ฒ๋ฆฌํฉ๋๋ค.
- ํตํฉ ํ
์คํธ: Spring Test๋ฅผ ์ฌ์ฉํ์ฌ ์ปดํฌ๋ํธ ๊ฐ์ ํตํฉ์ ํ
์คํธํฉ๋๋ค.
@SpringBootTest๋ฅผ ์ฌ์ฉํ์ฌ ์ ํ๋ฆฌ์ผ์ด์ ์ปจํ ์คํธ๋ฅผ ๋ก๋ํ๊ณ@Autowired๋ฅผ ์ฌ์ฉํ์ฌ ์์กด์ฑ์ ์ฃผ์ ํฉ๋๋ค. - ์๋ํฌ์๋ ํ ์คํธ: Selenium์ด๋ Cypress์ ๊ฐ์ ๋๊ตฌ๋ฅผ ์ฌ์ฉํ์ฌ ์ฌ์ฉ์ ๊ด์ ์์ ์ ์ฒด ์ ํ๋ฆฌ์ผ์ด์ ์ ํ ์คํธํฉ๋๋ค.
- ํ ์คํธ ์ฃผ๋ ๊ฐ๋ฐ (TDD): ์ค์ ์ฝ๋๋ฅผ ์์ฑํ๊ธฐ ์ ์ ํ ์คํธ๋ฅผ ์์ฑํ๋ TDD๋ฅผ ์ฑํํฉ๋๋ค.
์์: ์คํ๋ง ์ปดํฌ๋ํธ ๋จ์ ํ ์คํธํ๊ธฐ
@RunWith(MockitoJUnitRunner.class)
public class ProductServiceTest {
@InjectMocks
private ProductService productService;
@Mock
private ProductRepository productRepository;
@Test
public void testGetAllProducts() {
List<Product> products = Arrays.asList(new Product(), new Product());
Mockito.when(productRepository.findAll()).thenReturn(products);
List<Product> result = productService.getAllProducts();
assertEquals(2, result.size());
}
}
7. Spring WebFlux๋ก ๋ฐ์ํ ํ๋ก๊ทธ๋๋ฐ ๊ตฌํ
๋ฐ์ํ ํ๋ก๊ทธ๋๋ฐ์ ๋น๋๊ธฐ ๋ฐ์ดํฐ ์คํธ๋ฆผ๊ณผ ๋ณ๊ฒฝ ์ ํ๋ฅผ ๋ค๋ฃจ๋ ํ๋ก๊ทธ๋๋ฐ ํจ๋ฌ๋ค์์ ๋๋ค. Spring WebFlux๋ ๋ ผ๋ธ๋กํน, ์ด๋ฒคํธ ๊ธฐ๋ฐ ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ตฌ์ถํ๊ธฐ ์ํ ๋ฐ์ํ ํ๋ ์์ํฌ๋ฅผ ์ ๊ณตํฉ๋๋ค.
- ๋ฐ์ํ ํ์
์ฌ์ฉ: Reactor ๋ผ์ด๋ธ๋ฌ๋ฆฌ์
Flux๋ฐMonoํ์ ์ ์ฌ์ฉํ์ฌ ๋น๋๊ธฐ ๋ฐ์ดํฐ ์คํธ๋ฆผ์ ๋ํ๋ ๋๋ค. - ๋ ผ๋ธ๋กํน IO: ์ฃผ ์ค๋ ๋๋ฅผ ์ฐจ๋จํ์ง ์๊ณ ์์ฒญ์ ์ฒ๋ฆฌํ๊ธฐ ์ํด ๋ ผ๋ธ๋กํน IO ์์ ์ ์ฌ์ฉํฉ๋๋ค.
- ์ญ์๋ ฅ(Backpressure): ์์ฐ์๊ฐ ์๋น์๊ฐ ์ฒ๋ฆฌํ ์ ์๋ ๊ฒ๋ณด๋ค ๋น ๋ฅด๊ฒ ๋ฐ์ดํฐ๋ฅผ ๋ด๋ณด๋ด๋ ์ํฉ์ ์ฒ๋ฆฌํ๊ธฐ ์ํด ์ญ์๋ ฅ์ ๊ตฌํํฉ๋๋ค.
- ํจ์ํ ํ๋ก๊ทธ๋๋ฐ: ๊ตฌ์ฑ ๊ฐ๋ฅํ๊ณ ํ ์คํธ ๊ฐ๋ฅํ ์ฝ๋๋ฅผ ์์ฑํ๊ธฐ ์ํด ํจ์ํ ํ๋ก๊ทธ๋๋ฐ ์์น์ ์ฑํํฉ๋๋ค.
์์: ๋ฐ์ํ ๋ฐ์ดํฐ ์ ๊ทผ
@Repository
public interface ReactiveProductRepository extends ReactiveCrudRepository<Product, Long> {
Flux<Product> findByName(String name);
}
8. Spring Cloud๋ก ๋ง์ดํฌ๋ก์๋น์ค ๊ตฌ์ถํ๊ธฐ
Spring Cloud๋ ๋ง์ดํฌ๋ก์๋น์ค ์ํคํ ์ฒ๋ฅผ ๊ตฌ์ถํ๊ธฐ ์ํ ๋๊ตฌ ๋ฐ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ธํธ๋ฅผ ์ ๊ณตํฉ๋๋ค. ์๋น์ค ๊ฒ์, ๊ตฌ์ฑ ๊ด๋ฆฌ, ๋ด๊ฒฐํจ์ฑ๊ณผ ๊ฐ์ ์ผ๋ฐ์ ์ธ ๋ฌธ์ ์ ๋ํ ์๋ฃจ์ ์ ์ ๊ณตํ์ฌ ๋ถ์ฐ ์์คํ ๊ฐ๋ฐ์ ๋จ์ํํฉ๋๋ค.
- ์๋น์ค ๊ฒ์: ์๋น์ค ๊ฒ์์ ์ํด Spring Cloud Netflix Eureka๋ฅผ ์ฌ์ฉํฉ๋๋ค. ์๋น์ค๊ฐ ์์ ์ ๋ฑ๋กํ๊ณ ๋ค๋ฅธ ์๋น์ค๋ฅผ ๊ฒ์ํ ์ ์๊ฒ ํฉ๋๋ค.
- ๊ตฌ์ฑ ๊ด๋ฆฌ: ์ค์ ์ง์ค์ ๊ตฌ์ฑ ๊ด๋ฆฌ๋ฅผ ์ํด Spring Cloud Config๋ฅผ ์ฌ์ฉํฉ๋๋ค. ์ค์ ์ ์ฅ์์ ๊ตฌ์ฑ ์์ฑ์ ์ ์ฅํ๊ณ ๊ด๋ฆฌํ ์ ์์ต๋๋ค.
- API ๊ฒ์ดํธ์จ์ด: ์์ฒญ์ ์ ์ ํ ๋ง์ดํฌ๋ก์๋น์ค๋ก ๋ผ์ฐํ ํ๋ API ๊ฒ์ดํธ์จ์ด๋ก Spring Cloud Gateway๋ฅผ ์ฌ์ฉํฉ๋๋ค.
- ์ํท ๋ธ๋ ์ด์ปค: ๋ด๊ฒฐํจ์ฑ์ ์ํด Spring Cloud Circuit Breaker (Resilience4j ๋๋ Hystrix ์ฌ์ฉ)๋ฅผ ์ฌ์ฉํฉ๋๋ค. ์คํจํ ์๋น์ค๋ฅผ ๊ฒฉ๋ฆฌํ์ฌ ์ฐ์์ ์ธ ์คํจ๋ฅผ ๋ฐฉ์งํฉ๋๋ค.
์์: ์๋น์ค ๊ฒ์์ ์ํ Spring Cloud Eureka ์ฌ์ฉ
Eureka ์๋ฒ
@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class, args);
}
}
Eureka ํด๋ผ์ด์ธํธ
@SpringBootApplication
@EnableEurekaClient
public class ProductServiceApplication {
public static void main(String[] args) {
SpringApplication.run(ProductServiceApplication.class, args);
}
}
9. ์คํ๋ง์ ์ด์ฉํ ํด๋ผ์ฐ๋ ๋ค์ดํฐ๋ธ ๊ฐ๋ฐ
์คํ๋ง์ ํด๋ผ์ฐ๋ ๋ค์ดํฐ๋ธ ๊ฐ๋ฐ์ ๋งค์ฐ ์ ํฉํฉ๋๋ค. ๋ค์์ ๋ช ๊ฐ์ง ์ฃผ์ ๊ณ ๋ ค ์ฌํญ์ ๋๋ค:
- 12 ์์ ์ฑ(Twelve-Factor App): ํด๋ผ์ฐ๋ ๋ค์ดํฐ๋ธ ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ตฌ์ถํ๊ธฐ ์ํด 12 ์์ ์ฑ ๋ฐฉ๋ฒ๋ก ์ ์์น์ ๋ฐ๋ฆ ๋๋ค.
- ์ปจํ ์ด๋ํ: ์ฌ์ด ๋ฐฐํฌ ๋ฐ ํ์ฅ์ ์ํด ์ ํ๋ฆฌ์ผ์ด์ ์ Docker ์ปจํ ์ด๋๋ก ํจํค์งํฉ๋๋ค.
- ์ค์ผ์คํธ๋ ์ด์ : ์ปจํ ์ด๋ ์ค์ผ์คํธ๋ ์ด์ ์ ์ํด ์ฟ ๋ฒ๋คํฐ์ค๋ฅผ ์ฌ์ฉํฉ๋๋ค. ์ปจํ ์ด๋ํ๋ ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ฐฐํฌ, ํ์ฅ ๋ฐ ๊ด๋ฆฌ๋ฅผ ์๋ํํฉ๋๋ค.
- ๊ด์ฐฐ ๊ฐ๋ฅ์ฑ(Observability): ์ ํ๋ฆฌ์ผ์ด์ ์ ๋์์ ๋ํ ํต์ฐฐ๋ ฅ์ ์ป๊ธฐ ์ํด ๋ชจ๋ํฐ๋ง, ๋ก๊น ๋ฐ ์ถ์ ์ ๊ตฌํํฉ๋๋ค.
10. ์ฝ๋ ํ์ง ๋ฐ ์ ์ง๋ณด์์ฑ
๊ณ ํ์ง์ ์ ์ง๋ณด์ ๊ฐ๋ฅํ ์ฝ๋๋ฅผ ์์ฑํ๋ ๊ฒ์ ์ฅ๊ธฐ์ ์ธ ์ฑ๊ณต์ ๋งค์ฐ ์ค์ํฉ๋๋ค. ๋ค์์ ๋ช ๊ฐ์ง ๋ชจ๋ฒ ์ฌ๋ก์ ๋๋ค:
- ์ฝ๋ ๋ฆฌ๋ทฐ: ์ ์ฌ์ ์ธ ๋ฌธ์ ๋ฅผ ์๋ณํ๊ณ ์ฝ๋ ํ์ง์ ๋ณด์ฅํ๊ธฐ ์ํด ์ ๊ธฐ์ ์ธ ์ฝ๋ ๋ฆฌ๋ทฐ๋ฅผ ์ํํฉ๋๋ค.
- ์ฝ๋ ์คํ์ผ: Checkstyle์ด๋ SonarQube์ ๊ฐ์ ๋๊ตฌ๋ฅผ ์ฌ์ฉํ์ฌ ์ผ๊ด๋ ์ฝ๋ ์คํ์ผ์ ์ ์ฉํฉ๋๋ค.
- SOLID ์์น: ๋ชจ๋ํ๋๊ณ ์ ์ง๋ณด์ ๊ฐ๋ฅํ ์ฝ๋๋ฅผ ๋ง๋ค๊ธฐ ์ํด ๊ฐ์ฒด ์งํฅ ์ค๊ณ์ SOLID ์์น์ ๋ฐ๋ฆ ๋๋ค.
- DRY ์์น: DRY (Don't Repeat Yourself) ์์น์ ๋ฐ๋ผ ์ค๋ณต์ ํผํฉ๋๋ค.
- YAGNI ์์น: YAGNI (You Ain't Gonna Need It) ์์น์ ๋ฐ๋ผ ๋ถํ์ํ ๋ณต์ก์ฑ์ ์ถ๊ฐํ๋ ๊ฒ์ ํผํฉ๋๋ค.
๊ฒฐ๋ก
์คํ๋ง ๊ฐ๋ฐ์ ๋ง์คํฐํ๋ ค๋ฉด ํต์ฌ ์์น๊ณผ ๊ณ ๊ธ ๊ธฐ์ ์ ๋ํ ๊น์ ์ดํด๊ฐ ํ์ํฉ๋๋ค. ์คํ๋ง ๋ถํธ, ์คํ๋ง MVC, ์คํ๋ง ์นํ๋ญ์ค, ์คํ๋ง ๋ฐ์ดํฐ JPA, ์คํ๋ง ์ํ๋ฆฌํฐ, ์คํ๋ง ํด๋ผ์ฐ๋๋ฅผ ํ์ฉํ์ฌ ํ๋์ ์ธ ๊ธฐ์ ํ๊ฒฝ์ ์๊ตฌ๋ฅผ ์ถฉ์กฑํ๋ ํ์ฅ ๊ฐ๋ฅํ๊ณ ์ ์ง๋ณด์ ๊ฐ๋ฅํ๋ฉฐ ๊ฒฌ๊ณ ํ ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ตฌ์ถํ ์ ์์ต๋๋ค. ๋์์์ด ์งํํ๋ ์๋ฐ ๊ฐ๋ฐ ์ธ๊ณ์์ ์์ ๋๊ฐ๊ธฐ ์ํด ์ฝ๋ ํ์ง, ํ ์คํธ, ์ง์์ ์ธ ํ์ต์ ์ฐ์ ์ํ๋ ๊ฒ์ ์์ง ๋ง์ธ์. ์คํ๋ง ์ํ๊ณ์ ํ์ ๋ฐ์๋ค์ฌ ์๋ฐ ๊ฐ๋ฐ์๋ก์์ ์ ์ฌ๋ ฅ์ ์ต๋ํ ๋ฐํํ์ธ์.
์ด ๊ฐ์ด๋๋ ๊ณ ๊ธ ์คํ๋ง ๊ฐ๋ฐ ๊ธฐ์ ์ ํ์ํ๊ธฐ ์ํ ๊ฒฌ๊ณ ํ ๊ธฐ๋ฐ์ ์ ๊ณตํฉ๋๋ค. ์คํ๋ง ๋ฌธ์๋ฅผ ๊ณ์ ํ์ํ๊ณ , ์ปจํผ๋ฐ์ค์ ์ฐธ์ํ๋ฉฐ, ์คํ๋ง ์ปค๋ฎค๋ํฐ์ ๊ต๋ฅํ์ฌ ์ง์๊ณผ ์ ๋ฌธ์ฑ์ ์ฌํ์ํค์ธ์.