๐Ÿ“ GitHub ๋ฐ”๋กœ๊ฐ€๊ธฐ

Project Intro

1๏ธโƒฃ ์„œ๋น„์Šค ๊ธฐ๋Šฅ

์„œ๋น„์Šค๊ธฐ๋Šฅ
1. ํ‚ค์›Œ๋“œ๋กœ ์ƒํ’ˆ๊ฒ€์ƒ‰ โ†’ ๊ฒฐ๊ณผ ๋ชฉ๋ก์œผ๋กœ ๋ณด์—ฌ์ฃผ๊ธฐ
2. ๊ด€์‹ฌ์ƒํ’ˆ ๋“ฑ๋กํ•˜๊ธฐ
3. ๊ด€์‹ฌ์ƒํ’ˆ ์กฐํšŒํ•˜๊ธฐ
4. ๊ด€์‹ฌ์ƒํ’ˆ ์ตœ์ €๊ฐ€๋“ฑ๋กํ•˜๊ธฐ

2๏ธโƒฃ API ๋™์ž‘์ˆœ์„œ Architecture

3๏ธโƒฃ API ๋ช…์„ธ์„œ

๊ธฐ๋ŠฅMethodURLRequestResponse
๋ฉ”์ธํŽ˜์ด์ง€GET/api/shop-index.html
Query๋กœ ์ƒํ’ˆ๊ฒ€์ƒ‰, ์ƒํ’ˆ๊ฒ€์ƒ‰๊ฒฐ๊ณผ ๋ชฉ๋ก๋ฐ˜ํ™˜GET/api/search?query=๊ฒ€์ƒ‰์–ด-[
{
ย ย "title" : String,
ย ย "image" : String,
ย ย "link" : String,
ย ย "lprice" : int
},
โ€ขโ€ขโ€ข
]
๊ด€์‹ฌ์ƒํ’ˆ ๋“ฑ๋กPOST/api/products{
ย ย "title" : String,
ย ย "image" : String,
ย ย "link" : String,
ย ย "lprice" : int
}
{
ย ย "id" : Long,
ย ย "title" : String,
ย ย "image" : String,
ย ย "link" : String,
ย ย "lprice" : int,
ย ย "myprice" : int
}
๊ด€์‹ฌ์ƒํ’ˆ ์กฐํšŒGET/api/products-[
{
ย ย "id" : Long,
ย ย "title" : String,
ย ย "image" : String,
ย ย "link" : String,
ย ย "lprice" : int,
ย ย "myprice" : int
},
โ€ขโ€ขโ€ข
]
๊ด€์‹ฌ์ƒํ’ˆ ์ตœ์ €๊ฐ€ ๋“ฑ๋กPUT/api/products/{id}{
ย ย "myprice" : int
}
id

4๏ธโƒฃ Project Setting


1 NaverOpenAPI ๊ตฌํ˜„

๐Ÿ“Ž ์ƒํ’ˆ๊ฒ€์ƒ‰๊ฒฐ๊ณผ ๋ชฉ๋ก๋ฐ˜ํ™˜

1๏ธโƒฃ Naver > NaverApiController.java

@RestController
@RequiredArgsConstructor
@RequestMapping("/api")
public class NaverApiController {

    private final NaverApiService naverApiService;

    // ์ƒํ’ˆ๊ฒ€์ƒ‰๊ฒฐ๊ณผ ๋ชฉ๋ก๋ฐ˜ํ™˜
    @GetMapping("/search")
    public List<ItemDto> searchItems(@RequestParam String query) {
        return naverApiService.searchItems(query);
    }

}

2๏ธโƒฃ Naver > NaverApiService.java

@Service
@Slf4j
public class NaverApiService {

    public List<ItemDto> searchItems(String query) {

        RestTemplate restTemplate = new RestTemplate();

        HttpHeaders httpHeaders = new HttpHeaders();

        // HttpHeader -> Client Id/Pw ์‹ค์–ด ๋ณด๋ƒ„
        httpHeaders.add("X-Naver-Client-Id", "_VwwboxD7N9gzc6pnvuC");
        httpHeaders.add("X-Naver-Client-Secret", "e3kEsesnyl");
        String body = "";

        // HttpEntity -> header + body
        HttpEntity<String> requestEntity = new HttpEntity<String>(body, httpHeaders);

        // HttpResponse -> query + GET๋ฉ”์„œ๋“œ + Request(header+body)
        ResponseEntity<String> responseEntity = restTemplate.exchange(
                "https://openapi.naver.com/v1/search/shop.json?display=15&query="
                        + query, HttpMethod.GET, requestEntity, String.class);
        // display=15 : ๊ฒ€์ƒ‰๊ฒฐ๊ณผ ์ด 15๊ฐœ show
        // HttpEntity ํด๋ž˜์Šค : Http ์š”์ฒญ, ์‘๋‹ต์— ํ•ด๋‹นํ•˜๋Š” HttpHeader์™€ HttpBody๋ฅผ ๊ฐ€์ง
        // HttpEntity ํด๋ž˜์Šค๋ฅผ ์ƒ์†๋ฐ›์•„ ๊ตฌํ˜„ํ•œ ํด๋ž˜์Šค -> requestEntity, responseEntity

        // HttpStatus
        HttpStatusCode httpStatus = responseEntity.getStatusCode();
        int status = httpStatus.value();
        log.info("NAVER API Status Code : " + status);

        // Response Body -> method
        // responseEntity์˜ ๋ฐ”๋””์— ์žˆ๋Š” ๊ฐ’์„ -> String ํƒ€์ž… response์— ๋‹ด์•„์˜ด
        String response = responseEntity.getBody();

        return fromJSONtoItems(response);

    }

    // String(๊ฒ€์ƒ‰๊ฒฐ๊ณผ) -> Dto์— ๋‹ด๊ธฐ์œ„ํ•ด ๋ณ€ํ™˜ํ•˜๋Š” ๋ฉ”์„œ๋“œ
    public List<ItemDto> fromJSONtoItems(String response) {

        // JSONObject -> build.gradle์— ์ข…์†์„ฑ์ถ”๊ฐ€
        JSONObject jsonObject = new JSONObject(response); // String ๊ฒ€์ƒ‰๊ฒฐ๊ณผ -> JSONObject
        JSONArray items = jsonObject.getJSONArray("items"); // JSONObject -> JSONArray

        // Dto List ๊ฐ์ฒด์ƒ์„ฑ
        List<ItemDto> itemDtoList = new ArrayList<>();

        // JSONArray -> for๋ฌธ ๋Œ๋ฉด์„œ item ํ•˜๋‚˜์”ฉ ๊บผ๋‚ด -> ItemDto์— ๋ณ€ํ™˜
        for (int i = 0; i < items.length(); i++) {
            JSONObject itemJson = items.getJSONObject(i);
            ItemDto itemDto = ItemDto.of(itemJson);
            itemDtoList.add(itemDto);
        }

        return itemDtoList;

    }

}

3๏ธโƒฃ Naver > ItemDto.java

@Getter
@NoArgsConstructor
public class ItemDto {

    private String title;

    private String link;

    private String image;

    private int lprice;

    // ์ƒ์„ฑ์ž
    @Builder
    private ItemDto(String title, String link, String image, int lprice) {
        this.title = title;
        this.link = link;
        this.image = image;
        this.lprice = lprice;
    }

    // ์ •์ ํŒฉํ† ๋ฆฌ๋ฉ”์„œ๋“œ
    public static ItemDto of(JSONObject itemJson) {
        return ItemDto.builder()
                .title(itemJson.getString("title"))
                .link(itemJson.getString("link"))
                .image(itemJson.getString("image"))
                .lprice(itemJson.getInt("lprice"))
                .build();
    }

}

๐Ÿ“ ErrorIssue ๋ฐ”๋กœ๊ฐ€๊ธฐ

4๏ธโƒฃ Postman ํ™•์ธ


2 Product ์„ค๊ณ„

1๏ธโƒฃ Entity > Product.java

@Entity
@Getter @Setter
@NoArgsConstructor // ํŒŒ๋ผ๋ฏธํ„ฐ ์—†๋Š” ๊ธฐ๋ณธ์ƒ์„ฑ์ž
public class Product {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    // 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;

    // ์ƒ์„ฑ์ž
    @Builder
    private Product(String title, String image, String link, int lprice) {
        this.title = title;
        this.image = image;
        this.link = link;
        this.lprice = lprice;
        this.myprice = 0;
    }

    // ์ •์ ํŒฉํ† ๋ฆฌ๋ฉ”์„œ๋“œ
    public static Product of(ProductRequestDto productRequestDto) {
        return Product.builder()
                .title(productRequestDto.getTitle())
                .image(productRequestDto.getImage())
                .link(productRequestDto.getLink())
                .lprice(productRequestDto.getLprice())
                .build();
    }

}

2๏ธโƒฃ DTO

Dto > ProductResponseDto.java

@Getter
@NoArgsConstructor // ํŒŒ๋ผ๋ฏธํ„ฐ์—†๋Š” ๊ธฐ๋ณธ์ƒ์„ฑ์ž
public class ProductResponseDto {

    private Long id;

    private String title;

    private String link;

    private String image;

    private int lprice;

    private int myprice;

    // ์ƒ์„ฑ์ž
    @Builder
    private ProductResponseDto(Long id, String title, String link, String image, int lprice, int myprice) {
        this.id = id;
        this.title = title;
        this.link = link;
        this.image = image;
        this.lprice = lprice;
        this.myprice = myprice;
    }

    // ์ •์ ํŒฉํ† ๋ฆฌ๋ฉ”์„œ๋“œ
    public static ProductResponseDto of(Product product) {
        return ProductResponseDto.builder()
                .id(product.getId())
                .title(product.getTitle())
                .link(product.getLink())
                .image(product.getImage())
                .lprice(product.getLprice())
                .myprice(product.getMyprice())
                .build();
    }

}

Dto > ProductRequestDto.java

@Getter
@NoArgsConstructor // ํŒŒ๋ผ๋ฏธํ„ฐ์—†๋Š” ๊ธฐ๋ณธ์ƒ์„ฑ์ž
@AllArgsConstructor // ๋ชจ๋“  ํ•„๋“œ๋ฅผ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ๋ฐ›๋Š” ์ƒ์„ฑ์ž
public class ProductRequestDto {

    private String title;

    private String image;

    private String link;

    private int lprice;

}

Dto > ProductMypriceRequestDto.java

@Getter
@NoArgsConstructor
@AllArgsConstructor
public class ProductMypriceRequestDto {

    private int myprice;

}

3 ๊ด€์‹ฌ์ƒํ’ˆ๋“ฑ๋ก ์„ค๊ณ„

1๏ธโƒฃ Controller > AllInOneController.java

// JSON ํ˜•ํƒœ์˜ ๋ฐ์ดํ„ฐ๋ฐ˜ํ™˜
@RestController
@RequestMapping("/api")
public class AllInOneController {

    // ๊ด€์‹ฌ์ƒํ’ˆ ๋“ฑ๋ก
    @PostMapping("/products")
    public ProductResponseDto createProduct(@RequestBody ProductRequestDto productRequestDto) throws SQLException {

        //  RequestDto -> DB์— ์ €์žฅํ•  Entity๊ฐ์ฒด ์ƒ์„ฑ + ์ดˆ๊ธฐํ™”
        Product product = Product.of(productRequestDto);

        // DB ์—ฐ๊ฒฐ
        Connection connection = DriverManager.getConnection("jdbc:h2:mem:db", "mallang", "");

        // DB Query ์ž‘์„ฑ
        PreparedStatement preparedStatement = connection.prepareStatement("select max(id) as id from product");

        ResultSet resultSet = preparedStatement.executeQuery();

        if (resultSet.next()) {
            // product id ์„ค์ • = product ํ…Œ์ด๋ธ” ๋งˆ์ง€๋ง‰ Id + 1
            product.setId(resultSet.getLong("id") + 1);
        } else {
            throw new SQLException("product ํ…Œ์ด๋ธ”์˜ ๋งˆ์ง€๋ง‰ Id ๊ฐ’์„ ์ฐพ์•„์˜ค์ง€ ๋ชปํ–ˆ์Šต๋‹ˆ๋‹ค.");
        }

        preparedStatement = connection.prepareStatement("insert into product(id, title, image, link, lprice, myprice) values (?, ?, ?, ?, ?, ?)");
        preparedStatement.setLong(1, product.getId());
        preparedStatement.setString(2, product.getTitle());
        preparedStatement.setString(3, product.getImage());
        preparedStatement.setString(4, product.getLink());
        preparedStatement.setInt(5, product.getLprice());
        preparedStatement.setInt(6, product.getMyprice());

        // DB Query ์‹คํ–‰
        preparedStatement.executeUpdate();

        // DB ์—ฐ๊ฒฐํ•ด์ œ
        preparedStatement.close();

        connection.close();

        // Response ์ „์†ก
        return ProductResponseDto.of(product);

    }
}

2๏ธโƒฃ Postman ํ™•์ธ


4 ๊ด€์‹ฌ์ƒํ’ˆ์กฐํšŒ ์„ค๊ณ„

1๏ธโƒฃ Controller > AllInOneController.java

{
	// ๊ด€์‹ฌ์ƒํ’ˆ ์กฐํšŒ
    @GetMapping("/products")
    public List<ProductResponseDto> getProducts() throws SQLException {

        // ๋ฐ˜ํ™˜ํƒ€์ž… (ResponseDtoList) ๊ฐ์ฒด์ƒ์„ฑ
        List<ProductResponseDto> productResponseDtoList = new ArrayList<>();

        // DB ์—ฐ๊ฒฐ
        Connection connection = DriverManager.getConnection("jdbc:h2:mem:db", "mallang", "");

        // DB Query ์ž‘์„ฑ ๋ฐ ์‹คํ–‰
        Statement statement = connection.createStatement();

        ResultSet resultSet = statement.executeQuery("select * from product");

        // DB Query ๊ฒฐ๊ณผ -> productResponseDtoList ๋ณ€ํ™˜
        while (resultSet.next()) {
            Product product = new Product();

            product.setId(resultSet.getLong("id"));
            product.setTitle(resultSet.getString("title"));
            product.setImage(resultSet.getString("image"));
            product.setLink(resultSet.getString("link"));
            product.setLprice(resultSet.getInt("lprice"));
            product.setMyprice(resultSet.getInt("myprice"));

            productResponseDtoList.add(ProductResponseDto.of(product));
        }

        // DB ์—ฐ๊ฒฐํ•ด์ œ
        resultSet.close();
        connection.close();

        // Response ์ „์†ก
        return productResponseDtoList;

    }
}

2๏ธโƒฃ Postman ํ™•์ธ


5 ๊ด€์‹ฌ์ƒํ’ˆ์ตœ์ €๊ฐ€๋“ฑ๋ก ์„ค๊ณ„

1๏ธโƒฃ Controller > AllInOneController.java

{
	// ๊ด€์‹ฌ์ƒํ’ˆ ์ตœ์ €๊ฐ€ ๋“ฑ๋ก
    @PutMapping("/products/{id}")
    public Long updateProduct(@PathVariable Long id, @RequestBody ProductMypriceRequestDto productMypriceRequestDto) throws SQLException {

        // Entity ๊ฐ์ฒด์ƒ์„ฑ
        Product product = new Product();

        // DB ์—ฐ๊ฒฐ
        Connection connection = DriverManager.getConnection("jdbc:h2:mem:db", "mallang", "");

        // DB Query ์ž‘์„ฑ
        PreparedStatement preparedStatement = connection.prepareStatement("select  * from product where id = ?");

        preparedStatement.setLong(1, id);

        // DB Query ์‹คํ–‰
        ResultSet resultSet = preparedStatement.executeQuery();

        if (resultSet.next()) {
            product.setId(resultSet.getLong("id"));
            product.setTitle(resultSet.getString("title"));
            product.setImage(resultSet.getString("image"));
            product.setLink(resultSet.getString("link"));
            product.setLprice(resultSet.getInt("lprice"));
            product.setMyprice(resultSet.getInt("myprice"));
        } else {
            throw new NullPointerException("ํ•ด๋‹น Id๊ฐ€ ์กด์žฌํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.");
        }

        // DB Query ์ž‘์„ฑ
        preparedStatement = connection.prepareStatement("update product set myprice = ? where id = ?");

        preparedStatement.setInt(1, productMypriceRequestDto.getMyprice());
        preparedStatement.setLong(2, product.getId());

        // DB Query ์‹คํ–‰
        preparedStatement.executeUpdate();

        // DB ์—ฐ๊ฒฐํ•ด์ œ
        resultSet.close();
        preparedStatement.close();
        connection.close();

        // Response ์ „์†ก
        return product.getId();

    }
}

2๏ธโƒฃ Postman ํ™•์ธ


6 AllInOneController ํ•œ๊ณ„์ 

  • 1๊ฐœ์˜ ํด๋ž˜์Šค โ†’ ๋„ˆ๋ฌด ๋งŽ์€ ์–‘์˜ ์ฝ”๋“œ๊ฐ€ ์กด์žฌํ•จ

    • ์ฝ”๋“œ์ดํ•ด ์–ด๋ ค์›€
    • ์ฒ˜์Œ ~ ๋๊นŒ์ง€ ๋‹ค ์ฝ์–ด์•ผ ์ฝ”๋“œ์ดํ•ด ๊ฐ€๋Šฅ
  • ์ฝ”๋“œ ์ถ”๊ฐ€ ๋ฐ ๋ณ€๊ฒฝ ์š”์ฒญ โ†’ ๋Œ€์‘ ์–ด๋ ต๊ณ  ๋ฒˆ๊ฑฐ๋กœ์›€

1๏ธโƒฃ ์ ˆ์ฐจ์  ํ”„๋กœ๊ทธ๋ž˜๋ฐ

  • ์ดˆ๊ธฐ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ๋ฐฉ์‹

  • ์ปดํ“จํ„ฐ๊ฐ€ ํ•ด์•ผํ•˜๋Š” ์ผ โ†’ ์ˆœ์ฐจ์ ์œผ๋กœ ์ญ‰ ๋‚˜์—ดํ•ด๋†“์€ ์ฝ”๋”ฉ๋ฐฉ์‹

  • (ex. AllInOneController ํด๋ž˜์Šค API ์ฒ˜๋ฆฌ๋‚ด์šฉ)

2๏ธโƒฃ ๊ฐ์ฒด์ง€ํ–ฅ ํ”„๋กœ๊ทธ๋ž˜๋ฐ

  • ์†Œํ”„ํŠธ์›จ์–ด ๊ทœ๋ชจ๊ฐ€ ์ปค์ง€๋ฉด์„œ ๋“ฑ์žฅ

  • ํ•˜๋‚˜์˜ ์‚ฌ๋ฌผ(๊ฐ์ฒด) โ†’ ํ•˜๋‚˜์˜ ์˜๋ฏธ๋ฅผ ๋ถ€์—ฌํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ํ”„๋กœ๊ทธ๋ž˜๋ฐ

3๏ธโƒฃ ์ง€ํ–ฅํ•ด์•ผํ•  ํ”„๋กœ๊ทธ๋ž˜๋ฐ ๋ฐฉ์‹

๐Ÿ“Œ ์ฒ˜์Œ์—” ์ ˆ์ฐจ์ ํ”„๋กœ๊ทธ๋ž˜๋ฐ, ๋‚˜์ค‘์—” ๊ฐ์ฒด์ง€ํ–ฅ ํ”„๋กœ๊ทธ๋ž˜๋ฐ์œผ๋กœ ๋ฆฌํŒฉํ† ๋ง

  • ๋ฆฌํŒฉํ† ๋ง (Refactoring) : ๊ธฐ๋Šฅ ์ƒ ๋ณ€๊ฒฝ โŒ, ํ”„๋กœ๊ทธ๋ž˜๋ฐ ๊ตฌ์กฐ๋ฅผ ๊ฐœ์„ ํ•˜๋Š” ๊ฒƒ

    • โ‘  ํ•˜๋‚˜์˜ ํŒŒ์ผ์— ๋„ˆ๋ฌด ๋งŽ์€ ์ฝ”๋“œ๊ฐ€ ๋“ค์–ด๊ฐ€์ง€ ์•Š๋„๋ก
    • โ‘ก ์—ญํ• ๋ณ„๋กœ ์ฝ”๋“œ ๋ถ„๋ฆฌ
    • โ‘ข ์ฝ”๋“œ๋ฅผ ๊ฐ€๋…์„ฑ ์ข‹๊ฒŒ

profile
๐ŸฑSunyeon-Jeong, mallang developer๐Ÿฐ

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