[Spring Boot] 17. ์ƒํ’ˆ ๋“ฑ๋ก

shrยท2022๋…„ 3์›” 2์ผ
0

Spring

๋ชฉ๋ก ๋ณด๊ธฐ
16/23
post-thumbnail

์ƒํ’ˆ ๋“ฑ๋ก


๐Ÿ“ ํŒŒ์ผ ์—…๋กœ๋“œ ๋ฐฉ์‹์—๋Š” โ‘  ํ”„๋กœ์ ํŠธ ๋‚ด๋ถ€์— ํŒŒ์ผ์„ ์—…๋กœ๋“œํ•˜๋Š” ๋ฐฉ์‹๊ณผ โ‘ก ํ”„๋กœ์ ํŠธ ์™ธ๋ถ€์— ๋”ฐ๋กœ ์—…๋กœ๋“œ ํด๋”๋ฅผ ๋งŒ๋“ค์–ด์„œ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฐฉ์‹ ๋‘ ๊ฐ€์ง€๊ฐ€ ์žˆ๋‹ค. ์šฐ๋ฆฌ๋Š” โ‘ก๋ฒˆ ๋ฐฉ์‹์„ ์‚ฌ์šฉํ•œ๋‹ค. ํ•ด๋‹น ๋ฐฉ์‹์€ ํŒŒ์ผ์˜ ๊ฒฝ๋กœ๋ฅผ http://localhost:8081/images/์‚ฌ์ง„์ด๋ฆ„์œผ๋กœ ์ง€์ •ํ•œ๋‹ค.

  1. src/main/java - com.example.demo.controller.rest - ProductRestController

    package com.example.demo.controller.rest;
    
    import java.io.*;
    import java.nio.file.Files;
    
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.http.*;
    import org.springframework.web.bind.annotation.*;
    import org.springframework.web.multipart.*;
    
    import com.example.demo.dto.*;
    import com.example.demo.service.*;
    
    import lombok.*;
    
    @RequiredArgsConstructor
    @RestController
    public class ProductRestController {
    
        private final ProductService service;
    
        @Value("${ck.image.folder}")
        private String CKImageFolder;
        @Value("${product.image.folder}")
    		private String imageFolder;
    
        @PostMapping(value="/product/image", produces = MediaType.APPLICATION_JSON_VALUE)
        public ResponseEntity<CKResponse> ckImageUpload(MultipartFile upload) {
            CKResponse ckResponse = service.ckImageUpload(upload);
            return ResponseEntity.ok(ckResponse);
        }
    
        @GetMapping("/images/{imagename}") 
        public ResponseEntity<byte[]> showImage(@PathVariable String imagename) {
            File file = new File(CKImageFolder, imagename);
    			if (file.exists() == false)
    				file = new File(imageFolder, imagename);
    			if (file.exists() == false)
    				return null;
    
            HttpHeaders headers = new HttpHeaders();
            // ํŒŒ์ผ ํ™•์žฅ์ž๋ฅผ ์ž๋ฅด๊ณ , ํ™•์žฅ์ž์— ๋”ฐ๋ผ ์ด๋ฏธ์ง€์˜ ContentType์„ ์ง€์ •ํ•œ๋‹ค.
            String extension = imagename.substring(imagename.lastIndexOf(".")+1).toUpperCase();
            MediaType type = null;
            if (extension.equals("JPG") || extension.equals("JPEG"))
                type = MediaType.IMAGE_JPEG;
            else if (extension.equals("PNG"))
                type = MediaType.IMAGE_PNG;
            else if (extension.equals("GIF"))
                type = MediaType.IMAGE_GIF;
            else
                return null;
    
            headers.setContentType(type);
            headers.add("Content-Disposition", "inline;filename=" + imagename);
    
            try {
                return ResponseEntity.ok().headers(headers).body(Files.readAllBytes(file.toPath()));
            } catch (IOException e) {
                e.printStackTrace();
            }
            return null;
        }
    }

    ๐Ÿ“ ์‚ฌ์ง„์ด ck ์ด๋ฏธ์ง€ ํด๋”์— ์—†์œผ๋ฉด ์ƒํ’ˆ ์‚ฌ์ง„ ์ด๋ฏธ์ง€ ํด๋”์— ์žˆ๋Š”์ง€๋„ ์ฐพ๋Š”๋‹ค. ๋‘˜ ๋‹ค ์—†์„ ๋•Œ๋งŒ null์„ ๋ฆฌํ„ดํ•œ๋‹ค. ์šฐ๋ฆฌ๋Š” CK ์ด๋ฏธ์ง€ ํด๋”์™€ ๊ทธ๋ƒฅ ์ด๋ฏธ์ง€ ํด๋”์˜ ์ฒ˜๋ฆฌ๋ฅผ ํ•จ๊ป˜ ํ•ด ์ฃผ๊ณ  ์žˆ๋‹ค. (๐Ÿ—ฃ๏ธ ๋”ฐ๋กœ ์ฒ˜๋ฆฌํ•  ์ˆ˜๋„ ์žˆ์ง€๋งŒ ์šฐ๋ฆฌ๊ฐ€ ํ•˜๊ธฐ์—” ๋„ˆ๋ฌด ๋ณต์žกํ•˜๊ณ  ์–ด๋ ต๋‹ค.)


  1. Product ํ…Œ์ด๋ธ” ์ƒ์„ฑ

    create table product (
        pno number(7),
        vendor varchar2(20 char),
        name varchar2(20 char),
        info clob,
        imagename varchar2(100 char),
        price number(7),
        salesVolume number(7),
        countOfStar number(7),
        sumOfStar number(7),
        countOfReview number(7),
        stock number(7),
        categoryCode varchar2(3 char),
        constraint product_pk_pno primary key(pno) 
    );


    ์‹œํ€€์Šค๋„ ํ•จ๊ป˜ ์ƒ์„ฑํ•œ๋‹ค.

    create sequence product_seq;

  1. src/main/java - com.example.demo.entity - Product ์ƒ์„ฑ

    package com.example.demo.entity;
    
    import lombok.*;
    import lombok.experimental.*;
    
    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    @Builder
    @Accessors(chain=true)
    public class Product {
        private Integer pno;
        private String vendor;
        private String name;
        // ์ œํ’ˆ ์„ค๋ช… (ck์— ๋“ค์–ด๊ฐ„๋‹ค.)
        private String info;
        private String imagename;
        private Integer price;
        // ํŒ๋งค๋Ÿ‰
        private Integer salesVolume;
        // ๋ณ„์  (5์  ๋งŒ์ ) - ์ถœ๋ ฅํ•  ๋•Œ ํ‰๊ท ์„ ๊ณ„์‚ฐ
        private Integer sumOfStar;
        private Integer countOfStar;
        // ๋ฆฌ๋ทฐ ๊ฐœ์ˆ˜
        private Integer countOfReview;
        // ์žฌ๊ณ 
        private Integer stock;
        // ์นดํ…Œ๊ณ ๋ฆฌ ์†Œ๋ถ„๋ฅ˜ ์ฝ”๋“œ
        private String categoryCode;
    }

  1. src/main/resource - templates - product - add.html

    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="UTF-8" xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
        <title>Insert title here</title>
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css">
        <link rel="stylesheet" href="/css/main.css">
        <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
        <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/js/bootstrap.min.js"></script>
        <script src="//cdn.jsdelivr.net/npm/sweetalert2@11"></script>
        <script src="/ckeditor/ckeditor.js"></script>
        <script>
            // ์ƒํ’ˆ ์ด๋ฏธ์ง€ ๋ฏธ๋ฆฌ ๋ณด๊ธฐ
            function previewImage() {
                const file = $("#imagename")[0].files[0];
    
                // ์ด๋ฏธ์ง€ ํฌ๊ธฐ ์ œํ•œ (1MB)
                const maxSize = 1024*1024;
                if (file.size > maxSize) {
                    Swal.fire("์ด๋ฏธ์ง€ ํฌ๊ธฐ ์˜ค๋ฅ˜", "์ƒํ’ˆ ์ด๋ฏธ์ง€๋Š” 1MB๋ฅผ ๋„˜์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.", "error");
                    // input๊ณผ ์ด๋ฏธ์ง€ ๋ฏธ๋ฆฌ ๋ณด๊ธฐ ์˜์—ญ ์ดˆ๊ธฐํ™”
                    $('#imagename').val('');
                    $('#show_image').removeAttr('src');
                    return false;
                }
    
                // ์›น์€ ๋ฌธ์ž์—ด ๊ธฐ๋ฐ˜์ด๋ผ ๋ฐ”์ด๋„ˆ๋ฆฌ ๋ฐ์ดํ„ฐ๋ฅผ ์ฃผ๊ณ ๋ฐ›์œผ๋ ค๋ฉด base64 ๋ฐฉ์‹์œผ๋กœ ์ธ์ฝ”๋”ฉ์„ ํ•œ๋‹ค.
    
                // ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์˜ FileReader ๋‚ด์žฅ ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑ
                const reader = new FileReader();
                // file ๊ฐ์ฒด๋ฅผ base64 ๋ฐฉ์‹์œผ๋กœ ์ธ์ฝ”๋”ฉํ•ด์„œ result ์†์„ฑ์— ์ €์žฅ
                reader.readAsDataURL(file);
                reader.onload = function() {
                    $('#show_image').attr("src", reader.result);
                }
                return true;
            }
    
            $(document).ready(function() {
                $('#imagename').change(previewImage);
    
                // ๋Œ€๋ถ„๋ฅ˜ ์ถœ๋ ฅ
                $.ajax("/categories").done((categories)=>{
                    const $mainCategory = $('#main_category');
                    $.each(categories, function(idx, c) {
                        $('<option>').text(c.name).val(c.code).appendTo($mainCategory);
                    });
                });
    
                // ๋Œ€๋ถ„๋ฅ˜ ์„ ํƒ
                $('#main_category').change(function() {
                    $.ajax('/categories?code='+$('#main_category').val()).done((categories)=>{
    
                        // ๋Œ€๋ถ„๋ฅ˜๋ฅผ ๋ณ€๊ฒฝํ•  ๊ฒฝ์šฐ ์ค‘๋ถ„๋ฅ˜์™€ ์†Œ๋ถ„๋ฅ˜๋ฅผ ๋ชจ๋‘ ์ดˆ๊ธฐํ™”
                        const $mediumCategory = $('#medium_category');
                        $mediumCategory.empty();
                        $mediumCategory.append('<option value="-1" disabled selected>์ค‘๋ถ„๋ฅ˜ ์„ ํƒ</option>');
                        $('#minor_category').empty().append('<option value="-1" disabled selected>์†Œ๋ถ„๋ฅ˜ ์„ ํƒ</option>');
    
                        $.each(categories, function(idx, c) {
                            $('<option>').text(c.name).val(c.code).appendTo($mediumCategory);
                        });
                    });
                });
    
                // ์ค‘๋ถ„๋ฅ˜ ์„ ํƒ
                $('#medium_category').change(function() {
                    $.ajax('/categories?code='+$('#medium_category').val()).done((categories)=>{
                        const $minorCategory = $('#minor_category');
                        $minorCategory.empty();
                        $minorCategory.append('<option value="-1" disabled selected>์†Œ๋ถ„๋ฅ˜ ์„ ํƒ</option>');
                        $.each(categories, function(idx, c) {
                            $('<option>').text(c.name).val(c.code).appendTo($minorCategory);
                        });
                    });
                });
    
                // ํŒŒ์ผ ์—…๋กœ๋“œํ•  ๊ฒฝ๋กœ๋ฅผ ์ง€์ •
                const $ckUploadPath = "/product/image?_csrf=" + $('#_csrf').val();
                CKEDITOR.replace('info', {
                    filebrowserUploadUrl : $ckUploadPath
                });
            });
        </script>
    </head>
    <body>
    <div id="page">
        <header th:replace="/fragments/header.html">
        </header>
        <nav th:replace="/fragments/nav.html">
        </nav>
        <div id="main">
            <aside th:replace="/fragments/aside.html">
            </aside>
            <section>
                <form id="add_form" method="post" action="/product/add" enctype="multipart/form-data">
                    <input type="hidden" name="_csrf" th:value="${_csrf.token}" id="_csrf" />
                    <div>
                        ๋Œ€๋ถ„๋ฅ˜ <select id="main_category">
                            <option disabled selected value="-1">์„ ํƒํ•˜์„ธ์š”.</option>
                        </select>
                        ์ค‘๋ถ„๋ฅ˜ <select id="medium_category">
                            <option class="msg" disabled selected value="-1">๋Œ€๋ถ„๋ฅ˜๋ฅผ ์„ ํƒํ•˜์„ธ์š”.</option>
                        </select>
                        ์†Œ๋ถ„๋ฅ˜ <select id="minor_category" name="categoryCode">
                            <option class="msg" disabled selected value="-1">๋Œ€๋ถ„๋ฅ˜์™€ ์ค‘๋ถ„๋ฅ˜๋ฅผ ์„ ํƒํ•˜์„ธ์š”.</option>
                        </select>
                    </div>
                    <img id="show_image" height="240px">
                    <div class="form-group">
                        <label for="image">์ƒํ’ˆ ์ด๋ฏธ์ง€</label>
                        <input type="file" name="imagename" id="imagename" class="form-control" accept=".jpg,.jpeg,.png,.gif">
                    </div>
                    <div class="form-group">
                        <label for="manufacturer">์ œ์กฐ์‚ฌ</label>
                        <span id="manufacturer_msg"></span>
                        <div class="form-group">
                            <input type="text" class="form-control" id="vendor" name="vendor">
                        </div>
                    </div>	
                    <div class="form-group">
                        <label for="name">์ƒํ’ˆ๋ช…</label>
                        <span id="name_msg"></span>
                        <div class="form-group">
                            <input type="text" class="form-control" id="name" name="name">
                        </div>
                    </div>	
                    <div class="form-group">
                        <label for="price">๊ฐ€๊ฒฉ</label>
                        <span id="price_msg"></span>
                        <div class="form-group">
                            <input type="text" class="form-control" id="price" name="price">
                        </div>
                    </div>
                    <div class="form-group">
                        <textarea class="form-control" rows="5" id="info" name="info"></textarea>
                    </div>
                    <div class="form-group">
                        <label for="stock">์žฌ๊ณ ๋Ÿ‰ ์„ ํƒ</label>
                        <select id="stock" name="stock" class="form-control">
                            <option selected="selected">0</option>
                            <option>1</option>
                            <option>2</option>
                            <option>3</option>
                            <option>4</option>
                            <option>5</option>
                        </select>
                    </div>	
                    <div class="form-group" style="text-align: center;">
                        <button id="add" class="btn btn-info">์ถ”๊ฐ€</button>
                    </div>
                </form>
            </section>
        </div>
        <footer th:replace="/fragments/footer.html">
        </footer>
    </div>
    </body>
    </html>

    ๐Ÿ“ $("#imagename")[0]
    $(์„ ํƒ์ž)๋Š” jQuery ๊ฐ์ฒด๋ฅผ ์„ ํƒํ•˜๋Š” ์ฝ”๋“œ์ด๊ณ , $(์„ ํƒ์ž))[0]๋Š” ๋ถ™์—ฌ์„œ jQuery ์•ˆ์˜ html ์š”์†Œ๋ฅผ ๋ฝ‘์•„๋‚ผ ์ˆ˜ ์žˆ๋‹ค.


    ๐Ÿ“ files[0]
    <input type='file' id='imagename'> ์š”์†Œ๋Š” multiple ์กฐ๊ฑด์„ ์คฌ์„ ๋•Œ ์—ฌ๋Ÿฌ ๊ฐœ์˜ ํŒŒ์ผ์„ ์„ ํƒํ•  ์ˆ˜ ์žˆ์–ด์•ผ ํ•˜๋ฏ€๋กœ files๋ผ๋Š” ๋ฐฐ์—ด์„ ๊ฐ€์ง€๊ณ , ์ด ๋ฐฐ์—ด์€ ์„ ํƒํ•œ ํŒŒ์ผ์˜ ์ •๋ณด๋ฅผ ๋‹ด์€ ๊ฐ์ฒด๋“ค์„ ์ €์žฅํ•œ๋‹ค. multiple์„ ์“ฐ์ง€ ์•Š๊ณ  ์ด๋ฏธ์ง€๋ฅผ ํ•˜๋‚˜๋งŒ ์˜ฌ๋ฆด ๊ฒฝ์šฐ ํ•ด๋‹น ํŒŒ์ผ์€ files ๋ฐฐ์—ด์˜ 0๋ฒˆ์งธ์— ๋“ค์–ด ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•˜๋ฉด ๋œ๋‹ค.


  1. src/main/java - com.example.demo.dto - ProductDto ์ƒ์„ฑ

    package com.example.demo.dto;
    
    import org.springframework.web.multipart.*;
    
    import com.example.demo.entity.*;
    
    import lombok.*;
    
    @NoArgsConstructor(access = AccessLevel.PRIVATE)
    public class ProductDto {
    
        // ์ œํ’ˆ์„ ์ถ”๊ฐ€ํ•  ๋•Œ ์‚ฌ์šฉํ•˜๋Š” DTO
        @Data
        public static class Add {
            private String vendor;
            private String name;
            private String info;
            private Integer price;
            private Integer stock;
            private String categoryCode;
    
            private MultipartFile imagename;
    
            public Product toEntity() {
                return Product.builder().vendor(vendor).name(name).info(info).price(price).stock(stock)
                    .categoryCode(categoryCode).countOfReview(0).countOfStar(0).sumOfStar(0)
                    .salesVolume(0).build();
            }
        }
    }

    ๐Ÿ“ ํŒŒ์ผ์„ ์—…๋กœ๋“œํ•˜๋ฉด ์„œ๋ฒ„ ์ธก์—์„œ๋Š” MultipartFile๋กœ ๋ฐ›๋Š”๋‹ค. ๊ทธ๋Ÿฐ๋ฐ ์šฐ๋ฆฌ๋Š” ์ด๋ฏธ์ง€๋ฅผ ํ…Œ์ด๋ธ”์— ์ง์ ‘ ์ €์žฅํ•˜์ง€ ์•Š๊ณ , ํŒŒ์ผ๋ช…์„ ์ €์žฅํ•˜๋ ค๊ณ  ํ•œ๋‹ค. ๊ทธ๋Ÿฌ๊ธฐ ์œ„ํ•ด์„œ๋Š” imagename์ด ๋ฐ›์„ ๋•Œ๋Š” multipart, ์ €์žฅํ•  ๋•Œ๋Š” String์ด์–ด์•ผ ํ•œ๋‹ค. ๊ทธ๋ ‡๊ธฐ ๋•Œ๋ฌธ์— DTO๋กœ ๋นผ ์ค€ ๊ฒƒ์ด๋‹ค.


  1. src/main/java - com.example.demo.controller.mvc - ProductController

    package com.example.demo.controller.mvc;
    
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.*;
    
    import com.example.demo.dto.ProductDto;
    import com.example.demo.service.ProductService;
    
    import lombok.AllArgsConstructor;
    
    @AllArgsConstructor
    @Controller
    public class ProductController {
        private ProductService service;
        @GetMapping({"/", "/product/list"})
        public String index() {
            return "product/list";
        }
    
        // ์ƒํ’ˆ ์ถ”๊ฐ€
        @GetMapping("/product/add")
        public void add() {
        }
    
        @PostMapping("/product/add")
        public String add(ProductDto.Add dto) {
            service.add(dto);
            return "redirect:/";
        }
    }

  1. src/main/java - com.example.demo.service - ProductService

    package com.example.demo.service;
    
    import java.io.*;
    import java.util.List;
    import java.util.UUID;
    
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.stereotype.Service;
    import org.springframework.web.multipart.MultipartFile;
    
    import com.example.demo.dao.ProductDao;
    import com.example.demo.dto.*;
    import com.example.demo.dto.ProductDto.Add;
    import com.example.demo.entity.Product;
    
    import lombok.*;
    
    @RequiredArgsConstructor
    @Service
    public class ProductService {
        @Value("${ck.image.folder}")
        private String CKImageFolder;
    
        @Value("${ck.image.path}")
        private String ckImagepath;
    
        @Value("${product.image.folder}")
        private String imageFolder;
    
        @Value("${product.image.path}")
        private String imagePath;
    
        @Value("${default.image.name}")
        private String defaultImage;
    
        private final ProductDao productDao;
    
        public CKResponse ckImageUpload(MultipartFile image){
            if (image != null && image.isEmpty() == false) {
                String imageName = UUID.randomUUID() + "-" + image.getOriginalFilename();
    
                File file = new File(CKImageFolder, imageName);
    
                try {
                    image.transferTo(file);
                } catch (IllegalStateException | IOException e) {
                    e.printStackTrace();
                }
                return new CKResponse(1, imageName, ckImagepath + imageName);
            }
            return null;
        }
    
        public void add(Add dto) {
            Product product = dto.toEntity();
    
            // ์ด๋ฏธ์ง€์˜ ๊ฐ์ฒด๋ฅผ ๊ฐ€๋ฆฌํ‚ด
            MultipartFile image = dto.getImagename();
            product.setImagename(defaultImage);
    
            // MultipartFile์ด null์ด ์•„๋‹ˆ๊ณ  ์ด๋ฏธ์ง€ ํŒŒ์ผ(image/jpeg, image/png ๋“ฑ)์ด๋ผ๋ฉด
            if (image != null && image.isEmpty() == false && image.getContentType().toLowerCase().startsWith("image/")) {
                String imagename = UUID.randomUUID() + "-" + image.getOriginalFilename();
                File file = new File(imageFolder, imagename);
                try {
                    image.transferTo(file);
                    product.setImagename(imagename);
                } catch (IllegalStateException | IOException e) {
                    e.printStackTrace();
                }
            }
            productDao.save(product);
        }

  1. src/main/java - com.example.demo.dao - ProductDao

    package com.example.demo.dao;
    
    import java.util.List;
    
    import org.apache.ibatis.annotations.*;
    
    import com.example.demo.dto.ProductDto;
    import com.example.demo.entity.*;
    
    @Mapper
    public interface ProductDao {
    
        public void save(Product product);
    
        public int count(String categoryCode);
    
    }

  1. src/main/resources - mapper - productMapper.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace="com.example.demo.dao.ProductDao">
    
        <insert id="save" useGeneratedKeys="true" keyProperty="pno">
            <selectKey keyProperty="pno" order="BEFORE" resultType="int">
                select product_seq.nextval from dual
            </selectKey>
            insert into product(pno, vendor, name, info, imagename, price, salesVolume, countOfStar, sumOfStar, countOfReview, stock, categoryCode) 
            values(#{pno}, #{vendor}, #{name}, #{info}, #{imagename}, #{price}, #{salesVolume}, #{countOfStar}, #{sumOfStar}, #{countOfReview}, #{stock}, #{categoryCode})
        </insert>

    ๐Ÿ’ก selectKey

    ์‚ฌ์ „์— ์–ด๋–ค ํ‚ค ๊ฐ’์„ ๊ฐ€์ ธ์™€ ์ฆ๊ฐ€์‹œํ‚จ ํ›„ ์ž…๋ ฅํ•˜๊ฑฐ๋‚˜, ์ž…๋ ฅ ํ›„์— ์ฆ๊ฐ€๋œ ํ‚ค ๊ฐ’์„ ๊ฐ€์ ธ์˜ฌ ํ•„์š”๊ฐ€ ์žˆ์„ ๋•Œ ์‚ฌ์šฉํ•œ๋‹ค. selectKey๋ฅผ ์ด์šฉํ•˜๋ฉด ๋ณ„๋„์˜ ์ฟผ๋ฆฌ ๋กœ์ง์„ ๋“ฑ๋กํ•  ํ•„์š” ์—†์ด ํ•ด๋‹น ๋ฉ”์†Œ๋“œ์—์„œ ์ผ๊ด„ ์ฒ˜๋ฆฌ๊ฐ€ ๊ฐ€๋Šฅํ•˜๋‹ค.

    ์ถœ์ฒ˜

    mybatis์—์„œ selectKey ์‚ฌ์šฉ๋ฒ•

    ๐Ÿ—ฃ๏ธ ์ƒ์„ฑํ•œ ์‹œํ€€์Šค ๊ฐ’์ด ์„œ๋น„์Šค๋‚˜ ์ปจํŠธ๋กค๋Ÿฌ์—์„œ ํ•„์š”ํ•œ ๊ฒฝ์šฐ selectKey๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค. ์šฐ๋ฆฌ๋Š” ์‚ฌ์‹ค ์ง€๊ธˆ ํ•„์š” ์—†๋Š”๋ฐ selectKey ์จ ๋ณด๋ ค๊ณ  ์ผ๋‹ค.


profile
๋ชปํ•˜๋‹ค ๋ณด๋ฉด ์ž˜ํ•˜๊ฒŒ ๋˜๋Š” ๊ฑฐ์•ผ ๏ฝฅแด—๏ฝฅฬฅฬฅฬฅ

0๊ฐœ์˜ ๋Œ“๊ธ€