๋ฉ‹์‚ฌ Backend Plus 27์ผ์ฐจ ๐Ÿฆ

์‹ ์žฌ์›ยท2023๋…„ 11์›” 28์ผ

์˜ค๋Š˜๋„ ์˜ค์ „๊ณผ ์˜คํ›„ ๋‘˜๋‹ค ํŒ€ ํ”„๋กœ์ ํŠธ๋ฅผ ์ง„ํ–‰ํ•˜์˜€๊ณ , ์˜คํ›„์—๋Š” ํ”„๋กœ์ ํŠธ์— ํ•„์ˆ˜์ ์œผ๋กœ ๋ฆฌ์•กํŠธ ๊ธฐ์ˆ ์„ ์ถ”๊ฐ€ํ•ด์•ผํ•œ๋‹ค๊ณ  ํ•˜์—ฌ ์˜คํ›„์—๋Š” ์ฑ— GPT์™€ ํ•จ๊ป˜ ๋ฆฌ์•กํŠธ๋ฅผ ์ ์šฉํ•˜์˜€๋‹ค.

์˜ค์ „

  • ์˜ค์ „์—๋Š” ๋ฐฑ์—”๋“œ ๋‹จ ์ž‘์—…์„ ํ•˜์˜€๊ณ , ์ž‘์—…์„ ํ•˜๋ฉด์„œ ๋А๋‚€ ๊ถ๊ธˆ์ฆ์„ ์ •๋ฆฌ ํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

โžก ์„œ๋น„์Šค ํด๋ž˜์Šค๊ฐ€ ๋‹ค๋ฅธ ์„œ๋น„์Šค ํด๋ž˜์Šค๋ฅผ ์˜์กดํ•ด๋„ ๋˜๋Š”๊ฐ€์— ๋Œ€ํ•œ ๊ถ๊ธˆ์ฆ ์ด์˜€์Šต๋‹ˆ๋‹ค. ์•„๋ž˜ ์ฝ”๋“œ๋ฅผ ๋ณด๊ณ  ์ •๋ฆฌ ํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

์•„๋ž˜์˜ ์ฝ”๋“œ๋Š” ์ƒํ’ˆ์„ ์ด๋ฏธ์ง€์™€ ํ•จ๊ป˜ ๋“ฑ๋กํ• ์ˆ˜ ์žˆ๋Š” ๋ฉ”์†Œ๋“œ๊ฐ€ ์กด์žฌํ•˜๋Š” ์„œ๋น„์Šค ํด๋ž˜์Šค ์ž…๋‹ˆ๋‹ค.

@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class ProductServiceImpl implements ProductService {
    private final ProductRepository productRepository;
    private final ImageRepository imageRepository;
    
    @Value("${spring.servlet.multipart.location}")
    private String productImagePath;

@Override
@Transactional
    public void createProductWithImage(CreateRequest createRequest, 
    						MultipartFile images) throws IOException {
        if (!isImageExists(images)) {
            initProduct(createRequest);
        }
        if (isImageExists(images)) {
            String imageLocation = productImagePath;
            Product product = initProduct(createRequest);
            for (MultipartFile image : images) {
                String imageName = image.getOriginalFilename();
                String imagePath = imageLocation + imageName;

				// ์ด๋ฏธ์ง€ ๊ด€๋ จ IOException
                validateCreateDirectory(imageLocation);
                validateTransferImage(imagePath, image);

				// ์ƒํ’ˆ์˜ ์—ฐ๊ด€๊ด€๊ณ„์ธ ๋ฉ”์†Œ๋“œ๋ฅผ ํ†ตํ•ด ์ด๋ฏธ์ง€ ์ €์žฅ
                Image productImage = Product.createProductImage(imagePath);
                product.addProductImage(productImage);
                
                imageRepository.save(productImage);
            }
        }
    }
}

ํ•ด๋‹น ProductServiceImpl ํด๋ž˜์Šค์—์„œ๋Š” Product ๋ ˆํฌ์ง€ํ† ๋ฆฌ์™€ Image ๋ ˆํฌ์ง€ํ† ๋ฆฌ๋ฅผ ์˜์กด์„ฑ ์ฃผ์ž…์„ ๋ฐ›์•„ createProductWithImage ๋ฉ”์†Œ๋“œ๋ฅผ ํ†ตํ•ด Product ๋ฅผ ์ €์žฅํ•˜๊ณ  Image ๋ฅผ ์ €์žฅํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

์ด ๋ถ€๋ถ„์—์„œ ํ•˜๋‚˜์˜ ๋ฉ”์†Œ๋“œ๊ฐ€ ๋งŽ์€ ๊ธฐ๋Šฅ์„ ์ˆ˜ํ–‰ํ•˜๊ณ  ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•˜์˜€๊ณ , Image ๋ ˆํฌ์ง€ํ† ๋ฆฌ๋ฅผ ProductServiceImpl ํด๋ž˜์Šค์— ์˜์กดํ•  ํ•„์š”๊ฐ€ ์—†๋‹ค๊ณ  ์ƒ๊ฐ ํ•˜์˜€์Šต๋‹ˆ๋‹ค. โžก (ImageServiceImpl ํด๋ž˜์Šค๋ฅผ ์ƒ์„ฑ)

๊ฐœ์„ ํ•œ ์ฝ”๋“œ ์ž…๋‹ˆ๋‹ค.


@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class ProductServiceImpl implements ProductService {
    private final ProductRepository productRepository;
    private final ImageService imageService;

    @Override
    @Transactional
    public void createProductWithImage(CreateRequest createRequest,
    				MultipartFile images) throws IOException {
                    
        if (!isImageExists(images)) {
            initProduct(createRequest);
        }
        if (isImageExists(images)) {
            Product product = initProduct(createRequest);
            imageService.uploadImage(product, images);
        }
    }
}

โžก ProductServiceImpl ํด๋ž˜์Šค์—์„œ๋Š” ์ƒํ’ˆ๋งŒ ์ €์žฅํ•˜๋ฉฐ, imageService ํด๋ž˜์Šค์˜ uploadImage ๋ฉ”์†Œ๋“œ์— Product ๊ฐ์ฒด๋ฅผ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ๋„˜๊ธฐ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ์ฆ‰ ์ด๋ง์€ ์ด๋ฏธ์ง€๋ฅผ ์ €์žฅํ•˜๋Š” ์ฝ”๋“œ๋Š” ์•„๋ž˜์™€ ๊ฐ™์ด ImageService ์—์„œ ์ฒ˜๋ฆฌ ํ•œ๋‹ค๋Š” ๋œป ์ž…๋‹ˆ๋‹ค.

@Service
@RequiredArgsConstructor
public class ImageServiceImpl implements ImageService {
    private final ImageRepository imageRepository;

    @Value("${spring.servlet.multipart.location}")
    private String productImagePath;

	// Product ๊ฐ์ฒด๋ฅผ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ๋„˜๊ธฐ๋ฉด์„œ, ๋ฉ”์†Œ๋“œ๋ฅผ ๋ถ„๋ฆฌ ํ•˜์˜€๋‹ค.
    @Transactional
    public void uploadImage(Product product, 
    			MultipartFile image) throws IOException {
        String imageLocation = productImagePath;
        String imageName = image.getOriginalFilename();
        String imagePath = imageLocation + imageName;

		// ์ด๋ฏธ์ง€ ๊ด€๋ จ IOException
        validateCreateDirectory(imageLocation);
        validateTransferImage(imagePath, image);

		// ์ƒํ’ˆ์˜ ์—ฐ๊ด€๊ด€๊ณ„์ธ ๋ฉ”์†Œ๋“œ๋ฅผ ํ†ตํ•ด ์ด๋ฏธ์ง€ ์ €์žฅ
        Image productImage = Product.createProductImage(imagePath);
        product.addProductImage(productImage);
        imageRepository.save(productImage);
    }
}

๋งˆ๋ฌด๋ฆฌ

์—ฌ๋Ÿฌ ์ž๋ฃŒ์™€ ๋ธ”๋กœ๊ทธ๋ฅผ ์ฐพ์•„๋ณด๋ฉฐ ์—ฌ๋Ÿฌ ๊ธ€์ด ์žˆ์—ˆ์ง€๋งŒ, ์„œ๋น„์Šค ๋‹จ์—์„œ๋Š” ๋ ˆํฌ์ง€ํ† ๋ฆฌ๋งŒ ์˜์กด์„ฑ ์ฃผ์ž…์„ ํ•˜์—ฌ ์–ป๋Š” ์žฅ์ ์ด ์žˆ๊ธฐ๋„ ํ–ˆ์œผ๋ฉฐ, ์„œ๋น„์Šค ํด๋ž˜์Šค๋Š” ๋‹ค๋ฅธ ์„œ๋น„์Šค๋ฅผ ์˜์กด์„ฑ ์ฃผ์ž…์„ ๋ฐ›์•„ ์„œ๋น„์Šค ๋ ˆ์ด์–ด (๊ณ„์ธต)์„ ๋ถ„๋ฆฌํ•˜์—ฌ ์–ป๋Š” ์žฅ์ ์ด ์žˆ๋‹ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค. ์ƒํ™ฉ์— ๋งž์ถฐ ์žฅ๋‹จ์ ์„ ์ฐพ์•„๋ณด๋ฉฐ ์ ์šฉํ•˜๋ฉด ์ข‹์„ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

์˜คํ›„

vs code IDE (๊ฐœ๋ฐœ๋„๊ตฌ) ์—์„œ Node.js๋ฅผ ์„ค์น˜ํ•˜๊ณ  ๋ฆฌ์•กํŠธ๋ฅผ ์„ค์น˜ ํ•˜์˜€๋‹ค.

๋ฆฌ์•กํŠธ ๊ธฐ์ˆ ์— ๋Œ€ํ•ด์„œ๋Š” ์™„์ „ ๋ฌธ์™ธํ•œ์ด๋ผ ์ฑ— GPT์™€ ํŒ€์›๋ถ„๊ป˜ ๋„์›€์„ ๋ฐ›์œผ๋ฉด์„œ ํ”„๋ก ํŠธ ์ชฝ ์ž‘์—…์„ ํ•˜์˜€๊ณ , ๋‚ด์ผ๊นŒ์ง€ ๋„์›€์„ ๋ฐ›์•„์•ผ ๋  ๊ฒƒ ๊ฐ™๋‹ค..

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