[DIOS] ์ƒํ’ˆ ์ˆ˜์ • ํŽ˜์ด์ง€

๐Ÿ“Œ ์ƒํ’ˆ ์ˆ˜์ • ํŽ˜์ด์ง€(U)

- ์กฐ๊ฑด : ๊ด€๋ฆฌ์ž๋งŒ ์ ‘๊ทผ, ์ˆ˜์ • ๊ฐ€๋Šฅ

- ๊ธฐ๋Šฅ : ์ƒํ’ˆ์˜ ์ •๋ณด๋ฅผ ๋ถˆ๋Ÿฌ์˜จ ๋’ค ์ˆ˜์ •๋‚ด์šฉ ๋ฐ˜์˜

 

โœ… ์กฐ๊ฑด์„ค์ • :  ๊ด€๋ฆฌ์ž๋งŒ ์ˆ˜์ •ํŽ˜์ด์ง€์— ์ ‘๊ทผ ํ—ˆ์šฉํ•˜๊ธฐ

 

<a th:href="@{/goods/modify(gid=${goods.getIndex()})}" class="title" id="title"
   th:if="${session.user!=null && session.user.isAdmin}">
<span class="text">์ƒํ’ˆ ์ˆ˜์ •ํ•˜๊ธฐ</span>
</a>

์ƒํ’ˆ ์ฝ๊ธฐ html ์—์„œ '์ƒํ’ˆ์ˆ˜์ •ํ•˜๊ธฐ' ๋ฒ„ํŠผ์„ ๋งŒ๋“  ํƒœ๊ทธ์— ํƒ€์ž„๋ฆฌํ”„๋ฅผ ์ด์šฉํ•˜์—ฌ ๋กœ๊ทธ์ธํ•œ ์‚ฌ์šฉ์ž๊ฐ€ ๊ด€๋ฆฌ์ž์ผ๋•Œ์—๋งŒ ๋ณด์—ฌ์ง€๋„๋ก ์กฐ๊ฑด์„ ์„ค์ •ํ•ด์ค€๋‹ค.

<script
        th:if="${user == null || !user.isAdmin()}">
    alert('ํ•ด๋‹น ํŽ˜์ด์ง€๋Š” ๊ด€๋ฆฌ์ž๋งŒ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋Š” ํŽ˜์ด์ง€์ž…๋‹ˆ๋‹ค.');
    window.location.href='/dios/login';
</script>

 

๋งˆ์ฐฌ๊ฐ€์ง€๋กœ, ๋งŒ์•ฝ ๊ด€๋ฆฌ์ž๊ฐ€ ์•„๋‹Œ ์‚ฌ์šฉ์ž๊ฐ€ ์ž„์˜๋กœ ์ฃผ์†Œ์ฐฝ์„ ์ž…๋ ฅํ•ด์„œ ๋“ค์–ด๊ฐˆ ๊ฒฝ์šฐ๋„ ๋Œ€๋น„ํ•ด์•ผํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ƒํ’ˆ์ฝ๊ธฐhtml ์—์„œ๋„ <script> ํƒœ๊ทธ ์•ˆ์— ํƒ€์ž„๋ฆฌํ”„ ์กฐ๊ฑด์„ ์ด์šฉํ•˜์—ฌ alert ๊ฒฝ๊ณ ์ฐฝ์„ ๋„์šด ๋’ค ๋กœ๊ทธ์ธ ํ™”๋ฉด์œผ๋กœ ์ด๋™ํ•˜๋„๋ก ํ•œ๋‹ค.


โœ…  Controller ์—์„œ getModify ๋ฉ”์„œ๋“œ ์ž‘์„ฑ : ์ƒํ’ˆ์ˆ˜์ • html modelAndView๋กœ ์—ฐ๊ฒฐ 

 

โ„น๏ธ Controller : service์—์„œ ๊ฐ€์ ธ์˜จ ์ •๋ณด๋“ค์„ modelAndView ์— addObject ํ•˜์—ฌ html์— ์ •๋ณด ์ „๋‹ฌ

@RequestMapping(value = "modify",
   	method = RequestMethod.GET, produces = MediaType.TEXT_HTML_VALUE)
	public ModelAndView getModify(@SessionAttribute(value = "user", required = false) UserEntity user,
                                  @RequestParam(value = "gid", required = false) int gid) {

        ModelAndView modelAndView = new ModelAndView("goods/modify");
        ItemEntity item = new ItemEntity();
        item.setIndex(gid);

        Enum<?> result = this.goodsService.prepareModifyItem(item, user);
        modelAndView.addObject("item", item);
        modelAndView.addObject("result", result.name());
        SellerEntity[] sellers = this.goodsService.getSeller();
        ItemCategoryEntity[] categories = this.goodsService.getItemCategory();
        modelAndView.addObject("seller", sellers);
        modelAndView.addObject("category", categories);
        ItemColorEntity[] colors = this.goodsService.getItemColors(item.getIndex());
        modelAndView.addObject("colors", colors);
        ItemSizeEntity[] sizes = this.goodsService.getItemSize(item.getIndex());
        modelAndView.addObject("sizes", sizes);

        modelAndView.addObject("user", user);

        if (result == CommonResult.SUCCESS) {
            modelAndView.addObject("gid", item.getIndex());
        }

        return modelAndView;
    }

โ„น๏ธ Service(์ˆ˜์ • ์‚ฌ์ „ ์ค€๋น„) :  itemIndex ๊ฐ’์œผ๋กœ ์ˆ˜์ •ํ•˜๋ ค๋Š” item DB์— ์ €์žฅ๋˜์–ด์žˆ๋Š” ์ƒํ’ˆ์˜ ์ •๋ณด๋ฅผ mapper์—์„œ ๋ชจ๋‘ select ํ•˜์—ฌ ์ˆ˜์ •ํ•  ์•„์ดํ…œ ์ •๋ณด ๋ถˆ๋Ÿฌ์˜ค๊ธฐ

@Transactional
    public Enum<? extends IResult> prepareModifyItem(ItemEntity item, UserEntity user) {
        if (user == null) {
            return ModifyItemResult.NOT_SIGNED;
        }
        ItemEntity existingItem = this.goodsMapper.selectItemByIndex(item.getIndex());
        if (existingItem == null) {
            return ModifyItemResult.NO_SUCH_Item;
        }
        if (!user.isAdmin()) {
            return ModifyItemResult.NOT_ALLOWED;
        }
        item.setCategoryId(existingItem.getCategoryId());// ์ด๊ฒŒ cad
        item.setSellerIndex(existingItem.getSellerIndex());
        item.setItemName(existingItem.getItemName());
        item.setItemDetail(existingItem.getItemDetail());
        item.setPrice(existingItem.getPrice());
        item.setCount(existingItem.getCount());
        item.setCreatedOn(existingItem.getCreatedOn());
        item.setTitleImageData(existingItem.getTitleImageData());
        item.setTitleImageMime(existingItem.getTitleImageMime());
        item.setTitleImageName(existingItem.getTitleImageName());

        return CommonResult.SUCCESS;
    }

 

โ„น๏ธ HTML : Controller์—์„œ ์ „๋‹ฌ๋ฐ›์€ ์ •๋ณด๋ฅผthymeleaf ๋ฅผ ์ด์šฉํ•ด ๊ฐ’์„ ๊ฐ€์ ธ์˜จ๋‹ค.

<select id="itemCategory" th:name="itemCategory">
    <option value=""> ์ƒํ’ˆ ์ข…๋ฅ˜๋ฅผ ์„ ํƒํ•ด์ฃผ์„ธ์š”.</option>
    <option th:each="categories : ${category}"
            th:value="${categories.getId()}"
            th:text="${categories.getText()}" name="category"></option>
</select>

์˜ˆ์‹œ๋กœ ์ƒํ’ˆ๋ถ„๋ฅ˜๋ž€ ์ฝ”๋“œ๋งŒ ๊ฐ€์ ธ์™”์ง€๋งŒ, ๋ธŒ๋žœ๋“œ ์ •๋ณด๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ๋ฐฉ๋ฒ•์€ ์œ„์™€ ๋™์ผํ•˜๋‹ค.


โœ… ์˜ต์…˜ ๊ฐ’ ์ถ”๊ฐ€์ž…๋ ฅ(์ƒ‰์ƒ, ์‚ฌ์ด์ฆˆ)

โ„น๏ธ JavaScript: input ๋ฐ•์Šค์—์„œ ๋‚ด์šฉ์ž…๋ ฅ ํ›„  'enter'๋ฅผ ๋ˆ„๋ฅด๋ฉด selectBox๋กœ ์ž…๋ ฅํ•œ ๊ฐ’ ๋„˜๊ธฐ๊ธฐ

* ์‚ฌ์ด์ฆˆ์˜ ์ฝ”๋“œ๋„ ๋™์ผํ•˜๋‹ค

form['newColor'].addEventListener('keyup', e => {

    if (e.key === 'Enter') {

        if (form['newColor'].value === '') {
            return;
        }

        const optionElement = document.createElement('option');
        optionElement.innerText = e.target.value;
        optionElement.setAttribute('value', e.target.value);
        optionElement.dataset.virtual = "true";
        form['colors'].append(optionElement);
        e.target.value = '';
        e.target.focus();
    }
});

โœ… ์˜ต์…˜ ๊ฐ’ ์‚ญ์ œ(์ƒ‰์ƒ, ์‚ฌ์ด์ฆˆ) : 

โ„น๏ธ JavaScript: ์‚ญ์ œํ•  ์˜ต์…˜๊ฐ’์„ ์„ ํƒ ํ›„ ์‚ญ์ œ ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅผ ์‹œ Ajax ์š”์ฒญ์ด ์ƒ๊ธฐ๋„๋ก event ์ฝ”๋“œ ์ž‘์„ฑ

* ์‚ฌ์ด์ฆˆ์˜ ์ฝ”๋“œ๋„ ๋™์ผํ•˜๋‹ค

const colorDeleteButton = document.getElementById('colorDeleteButton');
colorDeleteButton.addEventListener('click', () => {
    const selectedOptions = Array.from(form['colors']
        .querySelectorAll(':scope > option'))
        .filter(x => x.selected);
    const formData = new FormData();
    let added = 0;
    for (let selectedOption of selectedOptions) {
        if (selectedOption.dataset.virtual === 'true') {
            selectedOption.remove();
        } else {
            formData.append('color', selectedOption.value);
            added++;
        }
    }
    if (added === 0) {
        return;
    }
    const xhr = new XMLHttpRequest();
    formData.append('itemIndex', form['itemIndex'].value);
    xhr.open('DELETE', './colors');
    xhr.onreadystatechange = () => {
        if (xhr.readyState === XMLHttpRequest.DONE) {
            if (xhr.status >= 200 && xhr.status < 300) {
                alert('์„ ํƒํ•œ ์ƒ‰์ƒ์ด ์‚ญ์ œ๋˜์—ˆ์Šต๋‹ˆ๋‹ค!');
                window.location.href = window.location.href;
            }
        }
    };
    xhr.send(formData);
});

โœ… ์ƒํ’ˆ ๋Œ€ํ‘œ ์ด๋ฏธ์ง€ ์ˆ˜์ •

โ„น๏ธ JavaScript

form.querySelector('[rel = "imageSelectButton"]').addEventListener('click', e => {
    e.preventDefault();
    form['images'].click();
});

form['images'].addEventListener('input', () => {
    const imageContainerElement = form.querySelector('[rel="imageContainer"]');
    imageContainerElement.querySelectorAll('img.image').forEach(x => x.remove());
    const imageSrc = URL.createObjectURL(form['images'].files[0]);
    document.getElementById('imgThumb').setAttribute('src', imageSrc);
})

โœ… Controller์—์„œ patchModify ๋ฉ”์„œ๋“œ ์ž‘์„ฑ

โ„น๏ธ Controller

@RequestMapping(value = "modify",
            method = RequestMethod.PATCH,
            produces = MediaType.APPLICATION_JSON_VALUE)
    @ResponseBody //xhr ๋กœ ๋ฐ˜ํ™˜ ๋ฐ›์„ ๊ฒƒ๋“ค์€ ๋ฌด์กฐ๊ฑด ResponseBody ๋ถ™์—ฌ์ค€๋‹ค.
    public String patchModify(@SessionAttribute(value = "user", required = false) UserEntity user,
                              @RequestParam(value = "gid") int gid,
                              @RequestParam(value = "newImage", required = false) MultipartFile newImage,
                              @RequestParam(value = "sizes", required = false) String[] sizes,
                              @RequestParam(value = "colors", required = false) String[] colors,
                              ItemEntity item) throws IOException {

        item.setIndex(gid);
        Enum<?> result = this.goodsService.ModifyItem(item, user, newImage);
        if (colors != null) {
            ItemColorEntity[] itemColors = new ItemColorEntity[colors.length];
            for (int i = 0; i < colors.length; i++) {
                itemColors[i] = new ItemColorEntity();
                itemColors[i].setItemIndex(item.getIndex());
                itemColors[i].setColor(colors[i]);
            }
            this.goodsService.addItemColors(itemColors);
        }

        if (sizes != null) {
            ItemSizeEntity[] itemSize = new ItemSizeEntity[sizes.length];
            for (int i = 0; i < sizes.length; i++) {
                itemSize[i] = new ItemSizeEntity();
                itemSize[i].setItemIndex(item.getIndex());
                itemSize[i].setSize(sizes[i]);
            }
            this.goodsService.addItemSizes(itemSize);
        }


        JSONObject responseObject = new JSONObject();
        responseObject.put("result", result.name().toLowerCase());
        if (result == CommonResult.SUCCESS) {
            responseObject.put("gid", gid);
        }
        return responseObject.toString();
    }

โ„น๏ธ Service(์ˆ˜์ •) 

์ƒํ’ˆ์ˆ˜์ •ํ•˜๋Š” ์‚ฌ์šฉ์ž๊ฐ€ ๋กœ๊ทธ์ธ์ด ๋˜์–ด์žˆ์ง€ ์•Š๊ฑฐ๋‚˜ ๊ด€๋ฆฌ์ž๊ฐ€ ์•„๋‹ˆ๋ผ๋ฉด ๊ถŒํ•œ์—†์Œ์„ ๋ฐ˜ํ™˜,

๊ธฐ์กด ์ƒํ’ˆ์„ ๋ถˆ๋Ÿฌ์™€ ์ƒˆ๋กœ์šด ์ •๋ณด set ํ•œ ๋’ค ์ƒํ’ˆ ์ˆ˜์ •ํ•œ ๊ฐฏ์ˆ˜์—๋”ฐ๋ผ ์„ฑ๊ณต์—ฌ๋ถ€ ๋ฐ˜ํ™˜ 

 @Transactional
    public Enum<? extends IResult> ModifyItem(ItemEntity item, UserEntity user, MultipartFile images) throws IOException {
        if (user == null) {
            return ModifyItemResult.NOT_SIGNED;
        }
        GoodsVo existingItem = this.goodsMapper.selectItemByIndex(item.getIndex());
        // ๊ธฐ์กด์— ์žˆ๋Š” title ์ด๋ฏธ์ง€ select
        ItemEntity existingTitleImage = this.goodsMapper.selectItemTitleImageByIndex(item.getIndex());
        if (existingItem == null) {
            return ModifyItemResult.NO_SUCH_Item;
        }
        if (!user.isAdmin()) {
            return ModifyItemResult.NOT_ALLOWED;
        }

        //์ƒˆ๋กœ ์ €์žฅํ•  ๋‚ด์šฉ์„ setํ•ด์ฃผ๊ธฐ > ์ˆ˜์ •๋œ ๋‚ด์šฉ์ด ์ €์žฅ๋จ
        existingItem.setCategoryId(item.getCategoryId());
        existingItem.setSellerIndex(item.getSellerIndex());
        existingItem.setItemName(item.getItemName());
        existingItem.setItemDetail(item.getItemDetail());
        existingItem.setPrice(item.getPrice());
        existingItem.setCount(item.getCount());
        existingItem.setCreatedOn(new Date());

        existingItem.setTitleImageData(images == null ? existingTitleImage.getTitleImageData() : images.getBytes()); //์กฐ๊ฑด ์•ˆ๊ฑธ๋ฉด ์ด๋ฏธ์ง€๊ฐ€ null ์ผ ๋•Œ ์˜ค๋ฅ˜๋œธ
        existingItem.setTitleImageMime(images == null ? existingTitleImage.getTitleImageMime(): images.getContentType()); //์กฐ๊ฑด ์•ˆ๊ฑธ๋ฉด ์ด๋ฏธ์ง€๊ฐ€ null ์ผ ๋•Œ ์˜ค๋ฅ˜๋œธ
        existingItem.setTitleImageName(images == null ? existingTitleImage.getTitleImageName() : images.getName()); //๋งˆ์ฐฌ๊ฐ€์ง€
        return this.goodsMapper.updateItem(existingItem) > 0
                ? CommonResult.SUCCESS
                : CommonResult.FAILURE;
    }

โ„น๏ธ JavaScript 

insert ํ• ๋•Œ DB์— ํ•„์š”ํ•œ ๊ฐ’๋“ค์„ formData์— append ํ•ด์ค€๋’ค

ajax ํ†ต์‹ ์„ ์ด์šฉํ•ด Controller ์—์„œ ๋ฐ›์€ ๊ฒฐ๊ณผ๊ฐ’์— ์•Œ๋งž๋Š” ๊ตฌํ˜„์„ ํ•ด์ฃผ๊ณ 

์„ฑ๊ณตํ–ˆ์„ ์‹œ์—๋Š” ์ƒํ’ˆ์ฝ๊ธฐ ํŽ˜์ด์ง€๋กœ ์ด๋™ํ•˜๋„๋ก ์ž‘์„ฑํ–ˆ๋‹ค.

submit.addEventListener('click', e => {

    e.preventDefault();

    if (form['itemName'].value === '') {
        alert('์ƒํ’ˆ ์ด๋ฆ„์„ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”.');
        form['itemName'].focus();
        return false;
    }
    if (editor.getData() === '') {
        alert('์ƒํ’ˆ ์ƒ์„ธํŽ˜์ด์ง€๋ฅผ ์ž…๋ ฅํ•ด ์ฃผ์„ธ์š”.');
        editor.focus();
        return false;
    }


    const xhr = new XMLHttpRequest();
    const formData = new FormData();
    formData.append('newImage', form['images'].files.length > 0 ? form['images'].files[0] : null);
    // formData.append('images', form['images'].files);// ์ด๋ฏธ์ง€ ํŒŒ์ผ
    formData.append('categoryId', itemCategory.options[itemCategory.selectedIndex].value); // ์ƒํ’ˆ ๋ถ„๋ฅ˜
    formData.append('itemName', form['itemName'].value);//์ƒํ’ˆ ์ด๋ฆ„
    formData.append('sellerIndex', seller.options[seller.selectedIndex].value);//์ƒํ’ˆ ๋ธŒ๋žœ๋“œ ๋„ค์ž„
    formData.append('itemDetail', editor.getData());
    formData.append('price', form['itemPrice'].value);
    formData.append('count', form['count'].value);

    form['colors'].querySelectorAll(':scope > option').forEach(option => {
        if (option.dataset.virtual === 'true') {
            formData.append('colors', option.value);
        }
    });

    form['sizes'].querySelectorAll(':scope > option').forEach(option => {
        if (option.dataset.virtual === 'true') {
            formData.append('sizes', option.value);
        }
    });

    xhr.open('PATCH', window.location.href);
    xhr.onreadystatechange = () => {
        if (xhr.readyState === XMLHttpRequest.DONE) {
            if (xhr.status >= 200 && xhr.status < 300) {
                const responseObject = JSON.parse(xhr.responseText);
                if (responseObject['result'] === 'not_allowed') {
                    alert('์ƒํ’ˆ ์ˆ˜์ •์˜ ๊ถŒํ•œ์ด ์—†์Šต๋‹ˆ๋‹ค.');
                } else if (responseObject['result'] === 'success') {
                    alert('์ƒํ’ˆ์ด ์ˆ˜์ •๋˜์—ˆ์Šต๋‹ˆ๋‹ค!');
                    window.location.href = './read?gid=' + responseObject['gid'];
                    form['image'].value;
                }
            } else {
                alert('์„œ๋ฒ„์™€ ํ†ต์‹ ํ•˜์ง€ ๋ชปํ•˜์˜€์Šต๋‹ˆ๋‹ค. ์ž ์‹œ ํ›„ ๋‹ค์‹œ ์‹œ๋„ํ•ด์ฃผ์„ธ์š”.');
            }
        }
    }
    xhr.send(formData);
});