Server/SpringBoot

[์›น๊ฐœ๋ฐœ์˜ ๋ด„, Spring] 4์ฃผ์ฐจ ๊ฐœ๋ฐœ์ผ์ง€(2) - <๋‚˜๋งŒ์˜ ์…€๋ ‰์ƒต> ์„œ๋ฒ„

meeeeejin 2021. 7. 16. 16:51

[์›น๊ฐœ๋ฐœ์˜ ๋ด„, Spring] 4์ฃผ์ฐจ ๊ฐœ๋ฐœ์ผ์ง€(1) - ๋„ค์ด๋ฒ„ ์‡ผํ•‘ API ์ด์šฉํ•˜๊ธฐ

 

 

์ด์ „ ํฌ์ŠคํŒ…์—์„œ <๋‚˜๋งŒ์˜ ์…€๋ ‰์ƒต>๋ฅผ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์„ค๊ณ„ํ–ˆ๋‹ค. 

 

๊ธฐ๋Šฅ Method URL return
ํ‚ค์›Œ๋“œ๋กœ ์ƒํ’ˆ ๊ฒ€์ƒ‰ํ•˜๊ณ  ๊ทธ ๊ฒฐ๊ณผ๋ฅผ ๋ชฉ๋ก์œผ๋กœ ๋ณด์—ฌ์ฃผ๊ธฐ GET /api/search?query=๊ฒ€์ƒ‰์–ด List<ItemDto>
๊ด€์‹ฌ ์ƒํ’ˆ ๋“ฑ๋กํ•˜๊ธฐ POST /api/products Product
๊ด€์‹ฌ ์ƒํ’ˆ ์กฐํšŒํ•˜๊ธฐ GET /api/products List<Product>
๊ด€์‹ฌ ์ƒํ’ˆ์— ๊ด€์‹ฌ ๊ฐ€๊ฒฉ ๋“ฑ๋กํ•˜๊ณ , ์ƒํ’ˆ ๊ฐ€๊ฒฉ์ด ๋” ๋‚ฎ์€ ๊ฒฝ์šฐ ํ‘œ์‹œํ•˜๊ธฐ PUT /api/products/{id} id

 

1. Controller

  • ProductRestController: ๊ด€์‹ฌ ์ƒํ’ˆ ๊ด€๋ จ ์ปจํŠธ๋กค๋Ÿฌ
  • SearchRequestContorller: ๊ฒ€์ƒ‰ ๊ด€๋ จ ์ปจํŠธ๋กค๋Ÿฌ

2. Service

  • ProductService: ๊ด€์‹ฌ ์ƒํ’ˆ ๊ฐ€๊ฒฉ ๋ณ€๊ฒฝ

3. Repository

  • Product: ๊ด€์‹ฌ ์ƒํ’ˆ ํ…Œ์ด๋ธ”
  • ProductRepository: ๊ด€์‹ฌ ์ƒํ’ˆ ์กฐํšŒ, ์ €์žฅ
  • ProductRequestDto: ๊ด€์‹ฌ ์ƒํ’ˆ ๋“ฑ๋กํ•˜๊ธฐ
  • ProductMypriceRequestDto: ๊ด€์‹ฌ ๊ฐ€๊ฒฉ ๋ณ€๊ฒฝํ•˜๊ธฐ
  • ItemDto: ๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ ์ฃผ๊ณ ๋ฐ›๊ธฐ

 

์ด๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ๊ด€์‹ฌ ์ƒํ’ˆ ์กฐํšŒํ•˜๊ธฐ -> ๊ด€์‹ฌ ์ƒํ’ˆ ์ €์žฅ ํ•˜๊ธฐ -> ๊ด€์‹ฌ ์ƒํ’ˆ ๊ฐ€๊ฒฉ ์„ค์ •ํ•˜๊ธฐ ์ˆœ์„œ๋Œ€๋กœ ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•ด๋ดค๋‹ค. 

 

 

 

<๋‚˜๋งŒ์˜ ์…€๋ ‰์ƒต> ์„œ๋ฒ„

๐Ÿ’ก Product Repository

์šฐ์„  ๊ด€์‹ฌ ์ƒํ’ˆ ํ…Œ์ด๋ธ”์ธ Product ํด๋ž˜์Šค๋ฅผ ๋งŒ๋“ค์–ด์•ผ ํ•œ๋‹ค. ํ•˜์ง€๋งŒ ๊ทธ์ „์— Product๊ฐ€ ์ƒ์†๋ฐ›์„ Timestamped ํด๋ž˜์Šค๋ฅผ ๋งŒ๋“ค์–ด๋ณด์ž~~

@Getter
@MappedSuperclass // ๋ฉค๋ฒ„ ๋ณ€์ˆ˜๊ฐ€ ์ปฌ๋Ÿผ์ด ๋˜๋„๋ก ํ•จ
@EntityListeners(AuditingEntityListener.class) // ๋ณ€๊ฒฝ๋˜์—ˆ์„ ๋•Œ ์ž๋™์œผ๋กœ ๊ธฐ๋ก
public abstract class Timestamped {
    @CreatedDate // ์ตœ์ดˆ ์ƒ์„ฑ ์‹œ์ 
    private LocalDateTime createdAt;

    @LastModifiedDate // ๋งˆ์ง€๋ง‰ ๋ณ€๊ฒฝ ์‹œ์ 
    private LocalDateTime modifiedAt;
}

 

์ด๋•Œ AuditingEntityListener๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด Application์—๊ฒŒ ์•Œ๋ ค์ค˜์•ผ ํ•œ๋‹ค. 

@EnableJpaAuditing // ์‹œ๊ฐ„ ์ž๋™ ๋ณ€๊ฒฝ์ด ๊ฐ€๋Šฅํ•˜๋„๋ก ํ•จ
@SpringBootApplication
public class Week04Application {
    public static void main(String[] args) {
        SpringApplication.run(Week04Application.class, args);
    }
}

 

์ด์ œ ์ฐ์œผ๋กœ Product ํด๋ž˜์Šค๋ฅผ ๋งŒ๋“ค์ž,, ๋ฐ๋ชจ๋ฅผ ๋ณด๋ฉด ์•Œ ์ˆ˜ ์žˆ๋“ฏ์ด ๊ด€์‹ฌ ์ƒํ’ˆ ํ…Œ์ด๋ธ”์— ์ €์žฅํ•ด์•ผ ํ•  ๋ฐ์ดํ„ฐ๋Š” title, image, link, lprice, myprice์ด๋‹ค. 

@Getter // get ํ•จ์ˆ˜๋ฅผ ์ผ๊ด„์ ์œผ๋กœ ๋งŒ๋“ค์–ด์ค๋‹ˆ๋‹ค.
@NoArgsConstructor // ๊ธฐ๋ณธ ์ƒ์„ฑ์ž๋ฅผ ๋งŒ๋“ค์–ด์ค๋‹ˆ๋‹ค.
@Entity // DB ํ…Œ์ด๋ธ” ์—ญํ• 
public class Product extends Timestamped{

	// ID์ž๋™ ์ƒ์„ฑ ๋ฐ ์ฆ๊ฐ€
    @GeneratedValue(strategy = GenerationType.AUTO) 
    @Id
    private Long id;

    @Column(nullable = false)
    private String title;

    @Column(nullable = false)
    private String image;

    @Column(nullable = false)
    private String link;

    @Column(nullable = false)
    private int lprice;

    @Column(nullable = false)
    private int myprice;
}

 

Product ํ…Œ์ด๋ธ”์„ ๋งŒ๋“ค์—ˆ์œผ๋‹ˆ Repository๋„ ํ˜ธ๋‹ค๋‹ฅ ๋งŒ๋“ ๋‹ค. 

public interface ProductRepository extends JpaRepository<Product, Long> {
}

 

 

 

๐Ÿ’ก Product Controller

์ด์ œ ๊ด€์‹ฌ ์ƒํ’ˆ์— ๊ด€ํ•œ ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ•  ์ปจํŠธ๋กค๋Ÿฌ๋ฅผ ๋งŒ๋“ค์–ด์•ผ ํ•œ๋‹ค. ์šฐ์„  ์ œ์ผ ๋งŒ๋งŒํ•ด ๋ณด์ด๋Š” "๊ด€์‹ฌ ์ƒํ’ˆ ์กฐํšŒํ•˜๊ธฐ" api๋ถ€ํ„ฐ ๋งŒ๋“ค์–ด๋ณด์ž. productRepsitory์—์„œ findAll์„ ํ•˜๋ฉด ๋  ๊ฒƒ ๊ฐ™๋‹ค. ๋งŒ๋“ค๊ณ  ๋‚˜์„œ ARC๋กœ ์‚ด์งœ์ฟต ์ž‘๋™์„ ํ™•์ธํ•˜์ž..^^

@RequiredArgsConstructor // final๋กœ ์„ ์–ธ๋œ ๋ฉค๋ฒ„ ๋ณ€์ˆ˜๋ฅผ ์ž๋™์œผ๋กœ ์ƒ์„ฑ
@RestController // JSON์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์ฃผ๊ณ ๋ฐ›์Œ์„ ์„ ์–ธ
public class ProductRestController {

    private final ProductRepository productRepository;

    // ๊ด€์‹ฌ ์ƒํ’ˆ ์กฐํšŒํ•˜๊ธฐ
    @GetMapping("/api/products")
    public List<Product> getProducts() {
        return productRepository.findAll();
    }
}

 

๋‹ค์Œ์€ "๊ด€์‹ฌ ์ƒํ’ˆ ๋“ฑ๋กํ•˜๊ธฐ"์ด๋‹ค. Product๋ฅผ ์ƒˆ๋กœ ์ƒ์„ฑํ•˜๊ธฐ ์œ„ํ•ด์„  DTO๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒŒ ์ข‹๋‹ค. Product์—์„œ id๋ฅผ ์ œ์™ธํ•œ ์นผ๋Ÿผ ๊ฐ’๋“ค์„ ๋ฉค๋ฒ„ ๋ณ€์ˆ˜๋กœ ๊ฐ–๋Š” ProductRequestDto๋ฅผ ๋งŒ๋“ ๋‹ค. 

@Getter
public class ProductRequestDto {
    private String title;
    private String link;
    private String image;
    private int lprice;
}

 

ProductRequestDto๋ฅผ ๋งŒ๋“ค์—ˆ์œผ๋‹ˆ ์ด์šฉํ•ด์ฃผ๋Š” ๊ฒŒ ์ธ์ง€์ƒ์ •! Product ํด๋ž˜์Šค์— ProductRequestDto๋ฅผ ์ด์šฉํ•˜๋Š” ์ƒ์„ฑ์ž๋ฅผ ์ถ”๊ฐ€ํ•œ๋‹ค. 

// ๊ด€์‹ฌ ์ƒํ’ˆ ์ƒ์„ฑ ์‹œ ์ด์šฉํ•จ
public Product(ProductRequestDto requestDto) {
    this.title = requestDto.getTitle();
    this.image = requestDto.getImage();
    this.link = requestDto.getLink();
    this.lprice = requestDto.getLprice();
    this.myprice = 0;
}

 

์ด์ œ ProductRestController์—์„œ ProductRequestDto๋ฅผ ์ด์šฉํ•ด Product๋ฅผ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค. ์–ผ๋ฅธ ProductRestController์— ๊ด€์‹ฌ ์ƒํ’ˆ ๋“ฑ๋ก api๋ฅผ ์ถ”๊ฐ€ํ•˜๋Ÿฌ ๊ฐ€์ž^^*

// ๊ด€์‹ฌ ์ƒํ’ˆ ๋“ฑ๋ก
@PostMapping("/api/products")
public Product createProduct(@RequestBodProductRequestDto requestDto) {
    Product product = new Product(requestDto);
    productRepository.save(product);
    return product;
}

 

 

 

๐Ÿ’ก Product Service

๋“œ๋””์–ด "๊ด€์‹ฌ ์ƒํ’ˆ์— ๊ด€์‹ฌ ๊ฐ€๊ฒฉ ๋“ฑ๋กํ•˜๊ณ , ์ƒํ’ˆ ๊ฐ€๊ฒฉ์ด ๋” ๋‚ฎ์€ ๊ฒฝ์šฐ ํ‘œ์‹œํ•˜๊ธฐ" ๊ธฐ๋Šฅ ํ•˜๋‚˜๋งŒ ๋‚จ์•˜๋‹ค. ๊ด€์‹ฌ ๊ฐ€๊ฒฉ์„ ๋“ฑ๋กํ•˜๋ ค๋ฉด ์ด ๋˜ํ•œ DTO๋ฅผ ์ด์šฉํ•ด์•ผ ๋œ๋‹ค. ProductMypriceRequestDto๋ฅผ ๋งŒ๋“ค์–ด์„œ myprice๋ฅผ ๊ฐ–๋„๋ก ํ•œ๋‹ค. 

@Getter
public class ProductMypriceRequestDto {
    private int myprice;
}

 

Product์—์„œ ProductMypriceRequestDto๋ฅผ ์ด์šฉํ•ด์„œ ๋‚ด๊ฐ€ ์„ค์ •ํ•œ ๊ฐ€๊ฒฉ ์ •๋ณด๋ฅผ update ํ•˜๋„๋ก ํ•จ์ˆ˜๋ฅผ ์ถ”๊ฐ€ํ•ด์ค˜์•ผ ํ•œ๋‹ค. ์ด ํ•จ์ˆ˜๋Š” ๊ด€์‹ฌ ๊ฐ€๊ฒฉ์„ ๋ณ€๊ฒฝํ•  ๋•Œ ์ด์šฉํ•  ๊ฒƒ์ด๋‹ค. 

// ๊ด€์‹ฌ ๊ฐ€๊ฒฉ ๋ณ€๊ฒฝ ์‹œ ์ด์šฉํ•จ
public void update(ProductMypriceRequestDto requestDto) {
    this.myprice = requestDto.getMyprice();
}

 

๊ด€์‹ฌ ์ƒํ’ˆ ๊ฐ€๊ฒฉ์„ ๋ณ€๊ฒฝํ•˜๋Š” ์„œ๋น„์Šค๋ฅผ ๋งŒ๋“ค์–ด์ค˜์•ผ ํ•œ๋‹ค. ๋งŒ๋“ค๊ฒŒ ์ •๋ง ๋Š์ž„์—†์ด ๋‚˜์™€์„œ ์Šฌ์Šฌ ์ง€์นœ๋‹ค... ํ•˜์ง€๋งŒ ์–ผ๋งˆ ์•ˆ ๋‚จ์•˜์–ด..! ProductService๋Š” ๊ด€์‹ฌ ์ƒํ’ˆ์˜ id์™€ ProductMypriceRequestDto๋ฅผ ๋ฐ›์•„์„œ ํ•ด๋‹น ์ƒํ’ˆ์˜ myprice ์ •๋ณด๋ฅผ update ํ•œ๋‹ค. ๋ฌผ๋ก  ์˜ˆ์™ธ ์ฒ˜๋ฆฌ๋„ ๋นผ๋จน์ง€ ์•Š๊ณ  ํ•ด์ค˜์•ผ ํ•œ๋‹ค. 

@RequiredArgsConstructor // final๋กœ ์„ ์–ธ๋œ ๋ฉค๋ฒ„ ๋ณ€์ˆ˜๋ฅผ ์ž๋™์œผ๋กœ ์ƒ์„ฑ
@Service // ์„œ๋น„์Šค์ž„์„ ์„ ์–ธ
public class ProductService {

    private final ProductRepository productRepository;

    @Transactional // ๋ฉ”์†Œ๋“œ ๋™์ž‘์ด SQL ์ฟผ๋ฆฌ๋ฌธ์ž„์„ ์„ ์–ธ
    public Long update(Long id, ProductMypriceRequestDto requestDto) {
        Product product = productRepository.findById(id).orElseThrow(
                () -> new NullPointerException("ํ•ด๋‹น ์•„์ด๋””๊ฐ€ ์กด์žฌํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.")
        );
        product.update(requestDto);
        return id;
    }
}

 

 

๋ฝ€๋‚˜์Šค๋กœ ์Šค์ผ€์ฅด๋Ÿฌ ๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ํ–ˆ๋‹ค. ๋ฌผ๋ก  ์ƒ๊ฐํ•ด๋ณด๋ฉด ํ•„์š”ํ•œ ๊ธฐ๋Šฅ์ด๋‹ค. ์ƒํ’ˆ ๊ฐ€๊ฒฉ์€ ์–ธ์ œ๋“  ๋ณ€๊ฒฝ๋  ์ˆ˜ ์žˆ์œผ๋‹ˆ๊นŒ..! ๋”ฐ๋ผ์„œ ๋งค์ผ ์ƒˆ๋ฒฝ 1์‹œ์— ๊ด€์‹ฌ ์ƒํ’ˆ์˜ title๋กœ ๊ฒ€์ƒ‰ํ•ด์„œ lprice๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๋Š” ์Šค์ผ€์ฅด๋Ÿฌ๋ฅผ ๋งŒ๋“ค์—ˆ๋‹ค. 

 

Scheduler ํด๋ž˜์Šค๋Š” utils ํŒจํ‚ค์ง€์— ์ƒ์„ฑํ–ˆ๋‹ค. 

@RequiredArgsConstructor // final ๋ฉค๋ฒ„ ๋ณ€์ˆ˜๋ฅผ ์ž๋™์œผ๋กœ ์ƒ์„ฑ
@Component // ์Šคํ”„๋ง์ด ํ•„์š” ์‹œ ์ž๋™์œผ๋กœ ์ƒ์„ฑํ•˜๋Š” ํด๋ž˜์Šค ๋ชฉ๋ก์— ์ถ”๊ฐ€
public class Scheduler {

    private final ProductRepository productRepository;
    private final ProductService productService;
    private final NaverShopSearch naverShopSearch;

    // ์ดˆ, ๋ถ„, ์‹œ, ์ผ, ์›”, ์ฃผ ์ˆœ์„œ
    @Scheduled(cron = "0 0 1 * * *")
    public void updatePrice() throws InterruptedException {
        System.out.println("๊ฐ€๊ฒฉ ์—…๋ฐ์ดํŠธ ์‹คํ–‰");
        // ์ €์žฅ๋œ ๋ชจ๋“  ๊ด€์‹ฌ์ƒํ’ˆ์„ ์กฐํšŒ
        List<Product> productList = productRepository.findAll();
        for (int i=0; i<productList.size(); i++) {
            // naver ์ œํ•œ์œผ๋กœ 1์ดˆ์— ํ•œ ์ƒํ’ˆ ์”ฉ ์กฐํšŒ
            TimeUnit.SECONDS.sleep(1);
            Product p = productList.get(i);
            // i ๋ฒˆ์งธ ๊ด€์‹ฌ ์ƒํ’ˆ์˜ ์ œ๋ชฉ์œผ๋กœ ๊ฒ€์ƒ‰์„ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.
            String resultString = naverShopSearch.search(title);
            List<ItemDto> itemDtoList = naverShopSearch.fromJSONtoItems(resultString);
            ItemDto itemDto = itemDtoList.get(0);
            // ๊ด€์‹ฌ ์ƒํ’ˆ ์ •๋ณด๋ฅผ ์—…๋ฐ์ดํŠธ
            Long id = p.getId();
            productService.updateBySearch(id, itemDto);
        }
    }
}

 

์ž˜ ๋ณด๋ฉด ๋งˆ์ง€๋ง‰์— ProductService์˜ updateBySearch ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์„ ์•Œ ์ˆ˜ ์žˆ๋‹ค. ํ•˜์ง€๋งŒ ๋‚œ ์ด๋Ÿฐ ๊ฑธ ๋งŒ๋“  ์ ์ด ์—†๋‹ค! ๊ทธ๋Ÿผ ์–ด๋–ป๊ฒŒ ํ•˜๋Š๋ƒ? ์„œ๋น„์Šค์— ๊ฐ€์„œ ๋งŒ๋“ค์–ด์ค˜์•ผ ํ•œ๋‹ค,,,

@Transactional // ๋ฉ”์†Œ๋“œ ๋™์ž‘์ด SQL ์ฟผ๋ฆฌ๋ฌธ์ž„์„ ์„ ์–ธ
  public Long updateBySearch(Long id, ItemDto itemDto) {
    Product product = productRepository.findById(id).orElseThrow(
            () -> new NullPointerException("ํ•ด๋‹น ์•„์ด๋””๊ฐ€ ์กด์žฌํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.")
    );
    product.updateByItemDto(itemDto);
    return id;
}

 

๋งˆ์ง€๋ง‰์œผ๋กœ ์Šคํ”„๋ง ๋ถ€ํŠธ์—์„œ ์Šค์ผ€์ค„๋Ÿฌ๊ฐ€ ์ž‘๋™ํ•˜๋„๋ก Application์— @EnableScheduling๋ฅผ ์ถ”๊ฐ€ํ•ด์•ผ ํ•œ๋‹ค. 

@EnableScheduling // ์Šคํ”„๋ง ๋ถ€ํŠธ์—์„œ ์Šค์ผ€์ค„๋Ÿฌ๊ฐ€ ์ž‘๋™ํ•˜๊ฒŒ ํ•จ
@EnableJpaAuditing
@SpringBootApplication
public class Week04Application {

    public static void main(String[] args) {
        SpringApplication.run(Week04Application.class, args);
    }

}

 

 

๐Ÿ’ก ๋งˆ๋ฌด๋ฆฌ

์ด๋ ‡๊ฒŒ <๋‚˜๋งŒ์˜ ์…€๋ ‰์ƒต> ๋ฐฑ์—”๋“œ ๊ฐœ๋ฐœ์ด ๋๋‚ฌ๋‹ค!!!๐ŸŽ‰๐ŸŽ‰ ํ”„๋ŸฐํŠธ์—”๋“œ์˜ html, css์™€ ajax๋กœ ์š”์ฒญ์„ ๋ณด๋‚ด๋Š” js ํŒŒ์ผ๋“ค์€ ์•„๋ž˜ ๊นƒํ—™์—์„œ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค. 

https://github.com/mjmjmj9840/Sparta-Spring-Basic/tree/main/week04

 

mjmjmj9840/Sparta-Spring-Basic

์ŠคํŒŒ๋ฅดํƒ€์ฝ”๋”ฉํด๋Ÿฝ์—์„œ ์›น๊ฐœ๋ฐœ์˜ ๋ด„, Spring ๊ฐ•์˜๋ฅผ ๋“ฃ๊ณ  ๊ณต๋ถ€ํ•œ ๋‚ด์šฉ์ž…๋‹ˆ๋‹ค. . Contribute to mjmjmj9840/Sparta-Spring-Basic development by creating an account on GitHub.

github.com

 

3์ฃผ์ฐจ ๊ฐ•์˜๊นŒ์ง€๋Š” ์–ผ๋ ˆ๋ฒŒ๋ ˆ ๋”ฐ๋ผ ํ–ˆ์—ˆ๋Š”๋ฐ ์ด๋ฒˆ ์ฃผ์ฐจ๋ถ€ํ„ฐ๋Š” ๊ฐ•์˜๋ฅผ ๋“ฃ๊ธฐ ์ „์— ๋จผ์ € ์ฝ”๋”ฉํ•ด๋ณด๋Š” ์‹์œผ๋กœ ํ•™์Šตํ–ˆ๋‹ค. ๋ถ„๋ช…ํžˆ ์ด์ „์— ๋น„์Šทํ•œ ์ฝ”๋“œ๋ฅผ ์งฐ์—ˆ๋Š”๋ฐ๋„ ์ž˜ ๊ธฐ์–ต์ด ์•ˆ ๋‚˜์„œ ๋ฉํ……๊ตฌ๋ฆฌ๊ฐ€ ๋œ ๊ธฐ๋ถ„์ด์—ˆ๋‹ค. ํ•˜์ง€๋งŒ ์ฒ˜์Œ๋ถ€ํ„ฐ ์ž˜ํ•  ์ˆ˜๋Š” ์—†๋Š” ๋ฒ•~ ๋‹คํ–‰ํžˆ ์ฒ˜์Œ๋ถ€ํ„ฐ ๋๊นŒ์ง€ ์ฝ”๋“œ๋ฅผ ์งœ์ง„ ๋ชปํ•ด๋„ ์ด์ „ ์ฃผ์ฐจ ์ฝ”๋“œ๋ฅผ ์ด๋ฒˆ ์ฃผ์ฐจ์— ๋งž๊ฒŒ ์‘์šฉ์€ ํ•  ์ˆ˜ ์žˆ์—ˆ๋‹ค..^^ ์•„์ง ์ƒˆ๋ผ๋ฐœ๊ฐ€๋ฝ ํ•˜๋‚˜ ๋‹ด๊ทผ ์ˆ˜์ค€์ด์ง€๋งŒ ์Šคํ”„๋ง์ด ์—„์ฒญ ๊ฐ„ํŽธํ•œ ํ”„๋ ˆ์ž„์›Œํฌ๋ผ๋Š” ๊ธฐ์šด์ด ๋ฒŒ์จ ๋Š๊ปด์ง„๋‹ค. 

 

 

 

 

 

์ฐธ๊ณ  ์ž๋ฃŒ: ์ŠคํŒŒ๋ฅดํƒ€์ฝ”๋”ฉํด๋Ÿฝ ์›น๊ฐœ๋ฐœ์˜ ๋ด„, Spring 4์ฃผ์ฐจ ๊ฐ•์˜์ž๋ฃŒ

 

 

 

728x90