- 저번 포스트에서 NaverAPI검색 호출을 다룬 것에 연장선!!
기능 | Method | URL | Request |
---|---|---|---|
메인페이지 | GET | /api/shop | - |
키워드로 상품 검색하고,보여주기 | GET | /api/search?query=검색어 | - |
관심 상품 등록하기 | POST | /api/products | { "title" : String, ”image” : String, "link" : String, "lprice" : int } |
관심 상품 조회하기 | GET | /api/products | - |
상품 최저가 등록하기 | PUT | /api/products/{id} | { "myprice" : int } |
title
,image
,link
,lprice
,myprice
의 정보가 필요함.
package com.sparta.myselectshopbeta.entity;
import com.sparta.myselectshopbeta.dto.ProductRequestDto;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import javax.persistence.*;
@Getter
@Setter
@Entity // DB 테이블 역할을 합니다.
@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;
public Product(ProductRequestDto requestDto) {
this.title = requestDto.getTitle();
this.image = requestDto.getImage();
this.link = requestDto.getLink();
this.lprice = requestDto.getLprice();
this.myprice = 0;
}
}
Entity
와DTO
를 왜 분리하는지 여기서 이유를 다시 짐작할 수 있었다.
ProductReqeustDto
: 상품 정보들을 담아 요청을 Wrapping 할 수 있는 DTO
package com.sparta.myselectshopbeta.dto;
import com.sparta.myselectshopbeta.entity.Product;
import lombok.Getter;
import lombok.NoArgsConstructor;
@Getter
@NoArgsConstructor
public class ProductResponseDto {
private Long id;
private String title;
private String link;
private String image;
private int lprice;
private int myprice;
public ProductResponseDto(Product product) {
this.id = product.getId();
this.title = product.getTitle();
this.link = product.getLink();
this.image = product.getImage();
this.lprice = product.getLprice();
this.myprice = product.getMyprice();
}
}
ProductResponseDto
: 상품 정보들을 조회 시 데이터를 담아 Wrapping하여 응답할 수 있는 DTO
package com.sparta.myselectshopbeta.dto;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
@Getter
@NoArgsConstructor
@AllArgsConstructor
public class ProductRequestDto {
// 관심상품명
private String title;
// 관심상품 썸네일 image URL
private String image;
// 관심상품 구매링크 URL
private String link;
// 관심상품의 최저가
private int lprice;
}
ProductMyPriceRequestDto
: 최저가 등록을 위한 DTO
package com.sparta.myselectshopbeta.dto;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
@Getter
@NoArgsConstructor
@AllArgsConstructor
public class ProductMypriceRequestDto {
private int myprice;
}
Service
->Repository
로 분리 하기 전! ,Jpa
를 사용하기 전!
AllInOneController
: Url 매핑과 DB접근 저장을 전부 한 Controller에 넣은 코드
package com.sparta.myselectshopbeta.controller;
import com.sparta.myselectshopbeta.dto.ProductMypriceRequestDto;
import com.sparta.myselectshopbeta.dto.ProductRequestDto;
import com.sparta.myselectshopbeta.dto.ProductResponseDto;
import com.sparta.myselectshopbeta.entity.Product;
import org.springframework.web.bind.annotation.*;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
@RestController
@RequestMapping("/api")
public class AllInOneController {
// 관심 상품 등록하기
@PostMapping("/products")
public ProductResponseDto createProduct(@RequestBody ProductRequestDto requestDto) throws SQLException {
// 요청받은 DTO 로 DB에 저장할 객체 만들기
Product product = new Product(requestDto);
// DB 연결
Connection connection = DriverManager.getConnection("jdbc:h2:mem:db", "sa", "");
// DB Query 작성
PreparedStatement ps = connection.prepareStatement("select max(id) as id from product");
ResultSet rs = ps.executeQuery();
if (rs.next()) {
// product id 설정 = product 테이블의 마지막 id + 1
product.setId(rs.getLong("id") + 1);
} else {
throw new SQLException("product 테이블의 마지막 id 값을 찾아오지 못했습니다.");
}
ps = connection.prepareStatement("insert into product(id, title, image, link, lprice, myprice) values(?, ?, ?, ?, ?, ?)");
ps.setLong(1, product.getId());
ps.setString(2, product.getTitle());
ps.setString(3, product.getImage());
ps.setString(4, product.getLink());
ps.setInt(5, product.getLprice());
ps.setInt(6, product.getMyprice());
// DB Query 실행
ps.executeUpdate();
// DB 연결 해제
ps.close();
connection.close();
// 응답 보내기
return new ProductResponseDto(product);
}
// 관심 상품 조회하기
@GetMapping("/products")
public List<ProductResponseDto> getProducts() throws SQLException {
List<ProductResponseDto> products = new ArrayList<>();
// DB 연결
Connection connection = DriverManager.getConnection("jdbc:h2:mem:db", "sa", "");
// DB Query 작성 및 실행
Statement stmt = connection.createStatement();
ResultSet rs = stmt.executeQuery("select * from product");
// DB Query 결과를 상품 객체 리스트로 변환
while (rs.next()) {
Product product = new Product();
product.setId(rs.getLong("id"));
product.setImage(rs.getString("image"));
product.setLink(rs.getString("link"));
product.setLprice(rs.getInt("lprice"));
product.setMyprice(rs.getInt("myprice"));
product.setTitle(rs.getString("title"));
products.add(new ProductResponseDto(product));
}
// DB 연결 해제
rs.close();
connection.close();
// 응답 보내기
return products;
}
// 관심 상품 최저가 등록하기
@PutMapping("/products/{id}")
public Long updateProduct(@PathVariable Long id, @RequestBody ProductMypriceRequestDto requestDto) throws SQLException {
Product product = new Product();
// DB 연결
Connection connection = DriverManager.getConnection("jdbc:h2:mem:db", "sa", "");
// DB Query 작성
PreparedStatement ps = connection.prepareStatement("select * from product where id = ?");
ps.setLong(1, id);
// DB Query 실행
ResultSet rs = ps.executeQuery();
if (rs.next()) {
product.setId(rs.getLong("id"));
product.setImage(rs.getString("image"));
product.setLink(rs.getString("link"));
product.setLprice(rs.getInt("lprice"));
product.setMyprice(rs.getInt("myprice"));
product.setTitle(rs.getString("title"));
} else {
throw new NullPointerException("해당 아이디가 존재하지 않습니다.");
}
// DB Query 작성
ps = connection.prepareStatement("update product set myprice = ? where id = ?");
ps.setInt(1, requestDto.getMyprice());
ps.setLong(2, product.getId());
// DB Query 실행
ps.executeUpdate();
// DB 연결 해제
rs.close();
ps.close();
connection.close();
// 응답 보내기 (업데이트된 상품 id)
return product.getId();
}
}
- 코드양이 너무 많아서 가독성이 떨어짐.
- 협업시 코드의 수정이 빈번하게 발생한다.
-> 하나의 파일에 너무 많은 코드가 들어가지 않게 하자.
-> 역할별로 코드를 분리하자
-> 코드를 좀 더 읽기 편하게 작성하자
- index.Html
<!doctype html>
<html lang="ko" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<link th:href="@{/style.css}" rel="stylesheet">
<script type="text/javascript" th:src="@{/basic.js}"></script>
<title>나만의 셀렉샵</title>
</head>
<body>
<div class="header">
Select Shop
</div>
<div class="nav">
<div class="nav-see active">
모아보기
</div>
<div class="nav-search">
탐색하기
</div>
</div>
<div id="see-area">
<div id="product-container">
<div class="product-card" onclick="window.location.href='https://spartacodingclub.kr'">
<div class="card-header">
<img src="https://shopping-phinf.pstatic.net/main_2085830/20858302247.20200602150427.jpg?type=f300"
alt="">
</div>
<div class="card-body">
<div class="title">
Apple 아이폰 11 128GB [자급제]
</div>
<div class="lprice">
<span>919,990</span>원
</div>
<div class="isgood">
최저가
</div>
</div>
</div>
</div>
</div>
<div id="search-area">
<div>
<input type="text" id="query">
</div>
<div id="search-result-box">
<div class="search-itemDto">
<div class="search-itemDto-left">
<img src="https://shopping-phinf.pstatic.net/main_2399616/23996167522.20200922132620.jpg?type=f300" alt="">
</div>
<div class="search-itemDto-center">
<div>Apple 아이맥 27형 2020년형 (MXWT2KH/A)</div>
<div class="price">
2,289,780
<span class="unit">원</span>
</div>
</div>
<div class="search-itemDto-right">
<img src="/images/icon-save.png" alt="" onclick='addProduct()'>
</div>
</div>
</div>
<div id="container" class="popup-container">
<div class="popup">
<button id="close" class="close">
X
</button>
<h1>⏰최저가 설정하기</h1>
<p>최저가를 설정해두면 선택하신 상품의 최저가가 떴을 때<br/> 표시해드려요!</p>
<div>
<input type="text" id="myprice" placeholder="200,000">원
</div>
<button class="cta" onclick="setMyprice()">설정하기</button>
</div>
</div>
</div>
</body>
</html>
- Css
body {
margin: 0px;
}
#search-result-box {
margin-top: 15px;
}
.search-itemDto {
width: 530px;
display: flex;
flex-direction: row;
align-content: center;
justify-content: space-around;
}
.search-itemDto-left img {
width: 159px;
height: 159px;
}
.search-itemDto-center {
display: flex;
flex-direction: column;
align-items: center;
justify-content: space-evenly;
}
.search-itemDto-center div {
width: 280px;
height: 23px;
font-size: 18px;
font-weight: normal;
font-stretch: normal;
font-style: normal;
line-height: 1.3;
letter-spacing: -0.9px;
text-align: left;
color: #343a40;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.search-itemDto-center div.price {
height: 27px;
font-size: 27px;
font-weight: 600;
font-stretch: normal;
font-style: normal;
line-height: 1;
letter-spacing: -0.54px;
text-align: left;
color: #E8344E;
}
.search-itemDto-center span.unit {
width: 17px;
height: 18px;
font-size: 18px;
font-weight: 500;
font-stretch: normal;
font-style: normal;
line-height: 1;
letter-spacing: -0.9px;
text-align: center;
color: #000000;
}
.search-itemDto-right {
display: inline-block;
height: 100%;
vertical-align: middle
}
.search-itemDto-right img {
height: 25px;
width: 25px;
vertical-align: middle;
margin-top: 60px;
cursor: pointer;
}
input#query {
padding: 15px;
width: 526px;
border-radius: 2px;
background-color: #e9ecef;
border: none;
background-image: url('../images/icon-search.png');
background-repeat: no-repeat;
background-position: right 10px center;
background-size: 20px 20px;
}
input#query::placeholder {
padding: 15px;
}
button {
color: white;
border-radius: 4px;
border-radius: none;
}
.popup-container {
display: none;
position: fixed;
top: 0;
left: 0;
bottom: 0;
right: 0;
background-color: rgba(0, 0, 0, 0.5);
align-items: center;
justify-content: center;
}
.popup-container.active {
display: flex;
}
.popup {
padding: 20px;
box-shadow: 2px 2px 10px rgba(0, 0, 0, 0.3);
position: relative;
width: 370px;
height: 190px;
border-radius: 11px;
background-color: #ffffff;
}
.popup h1 {
margin: 0px;
font-size: 22px;
font-weight: 500;
font-stretch: normal;
font-style: normal;
line-height: 1;
letter-spacing: -1.1px;
color: #000000;
}
.popup input {
width: 330px;
height: 39px;
border-radius: 2px;
border: solid 1.1px #dee2e6;
margin-right: 9px;
margin-bottom: 10px;
padding-left: 10px;
}
.popup button.close {
position: absolute;
top: 15px;
right: 15px;
color: #adb5bd;
background-color: #fff;
font-size: 19px;
border: none;
}
.popup button.cta {
width: 369.1px;
height: 43.9px;
border-radius: 2px;
background-color: #15aabf;
border: none;
}
#search-area, #see-area {
width: 530px;
margin: auto;
}
.nav {
width: 530px;
margin: 30px auto;
display: flex;
align-items: center;
justify-content: space-around;
}
.nav div {
cursor: pointer;
}
.nav div.active {
font-weight: 700;
}
.header {
background-color: #15aabf;
color: white;
text-align: center;
padding: 50px;
font-size: 45px;
font-weight: bold;
}
#product-container {
grid-template-columns: 100px 50px 100px;
grid-template-rows: 80px auto 80px;
column-gap: 10px;
row-gap: 15px;
}
.product-card {
width: 300px;
margin: auto;
cursor: pointer;
}
.product-card .card-header {
width: 300px;
}
.product-card .card-header img {
width: 300px;
}
.product-card .card-body {
margin-top: 15px;
}
.product-card .card-body .title {
font-size: 15px;
font-weight: normal;
font-stretch: normal;
font-style: normal;
line-height: 1;
letter-spacing: -0.75px;
text-align: left;
color: #343a40;
margin-bottom: 10px;
}
.product-card .card-body .lprice {
font-size: 15.8px;
font-weight: normal;
font-stretch: normal;
font-style: normal;
line-height: 1;
letter-spacing: -0.79px;
color: #000000;
margin-bottom: 10px;
}
.product-card .card-body .lprice span {
font-size: 21.4px;
font-weight: 600;
font-stretch: normal;
font-style: normal;
line-height: 1;
letter-spacing: -0.43px;
text-align: left;
color: #E8344E;
}
.product-card .card-body .isgood {
margin-top: 10px;
padding: 10px 20px;
color: white;
border-radius: 2.6px;
background-color: #ff8787;
width: 42px;
}
.none {
display: none;
}
- basic.js
let targetId;
$(document).ready(function () {
// id 가 query 인 녀석 위에서 엔터를 누르면 execSearch() 함수를 실행하라는 뜻입니다.
$('#query').on('keypress', function (e) {
if (e.key == 'Enter') {
execSearch();
}
});
$('#close').on('click', function () {
$('#container').removeClass('active');
})
$('.nav div.nav-see').on('click', function () {
$('div.nav-see').addClass('active');
$('div.nav-search').removeClass('active');
$('#see-area').show();
$('#search-area').hide();
})
$('.nav div.nav-search').on('click', function () {
$('div.nav-see').removeClass('active');
$('div.nav-search').addClass('active');
$('#see-area').hide();
$('#search-area').show();
})
$('#see-area').show();
$('#search-area').hide();
showProduct();
})
function numberWithCommas(x) {
return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
}
function execSearch() {
/**
* 검색어 input id: query
* 검색결과 목록: #search-result-box
* 검색결과 HTML 만드는 함수: addHTML
*/
// 1. 검색창의 입력값을 가져온다.
let query = $('#query').val();
// 2. 검색창 입력값을 검사하고, 입력하지 않았을 경우 focus.
if (query == '') {
alert('검색어를 입력해주세요');
$('#query').focus();
return;
}
// 3. GET /api/search?query=${query} 요청
$.ajax({
type: 'GET',
url: `/api/search?query=${query}`,
success: function (response) {
$('#search-result-box').empty();
// 4. for 문마다 itemDto를 꺼내서 HTML 만들고 검색결과 목록에 붙이기!
for (let i = 0; i < response.length; i++) {
let itemDto = response[i];
let tempHtml = addHTML(itemDto);
$('#search-result-box').append(tempHtml);
}
}
})
}
function addHTML(itemDto) {
/**
* class="search-itemDto" 인 녀석에서
* image, title, lprice, addProduct 활용하기
* 참고) onclick='addProduct(${JSON.stringify(itemDto)})'
*/
return `<div class="search-itemDto">
<div class="search-itemDto-left">
<img src="${itemDto.image}" alt="">
</div>
<div class="search-itemDto-center">
<div>${itemDto.title}</div>
<div class="price">
${numberWithCommas(itemDto.lprice)}
<span class="unit">원</span>
</div>
</div>
<div class="search-itemDto-right">
<img src="../images/icon-save.png" alt="" onclick='addProduct(${JSON.stringify(itemDto)})'>
</div>
</div>`
}
function addProduct(itemDto) {
/**
* modal 뜨게 하는 법: $('#container').addClass('active');
* data를 ajax로 전달할 때는 두 가지가 매우 중요
* 1. contentType: "application/json",
* 2. data: JSON.stringify(itemDto),
*/
// 1. POST /api/products 에 관심 상품 생성 요청
$.ajax({
type: "POST",
url: '/api/products',
contentType: "application/json",
data: JSON.stringify(itemDto),
success: function (response) {
// 2. 응답 함수에서 modal을 뜨게 하고, targetId 를 reponse.id 로 설정
$('#container').addClass('active');
targetId = response.id;
}
})
}
function showProduct() {
/**
* 관심상품 목록: #product-container
* 검색결과 목록: #search-result-box
* 관심상품 HTML 만드는 함수: addProductItem
*/
// 1. GET /api/products 요청
$.ajax({
type: 'GET',
url: '/api/products',
success: function (response) {
// 2. 관심상품 목록, 검색결과 목록 비우기
$('#product-container').empty();
$('#search-result-box').empty();
// 3. for 문마다 관심 상품 HTML 만들어서 관심상품 목록에 붙이기!
for (let i = 0; i < response.length; i++) {
let product = response[i];
let tempHtml = addProductItem(product);
$('#product-container').append(tempHtml);
}
}
})
}
function addProductItem(product) {
// link, image, title, lprice, myprice 변수 활용하기
return `<div class="product-card"token interpolation">${product.link}'">
<div class="card-header">
<img src="${product.image}"
alt="">
</div>
<div class="card-body">
<div class="title">
${product.title}
</div>
<div class="lprice">
<span>${numberWithCommas(product.lprice)}</span>원
</div>
<div class="isgood ${product.lprice > product.myprice ? 'none' : ''}">
최저가
</div>
</div>
</div>`;
}
function setMyprice() {
/**
* 1. id가 myprice 인 input 태그에서 값을 가져온다.
* 2. 만약 값을 입력하지 않았으면 alert를 띄우고 중단한다.
* 3. PUT /api/product/${targetId} 에 data를 전달한다.
* 주의) contentType: "application/json",
* data: JSON.stringify({myprice: myprice}),
* 빠뜨리지 말 것!
* 4. 모달을 종료한다. $('#container').removeClass('active');
* 5, 성공적으로 등록되었음을 알리는 alert를 띄운다.
* 6. 창을 새로고침한다. window.location.reload();
*/
// 1. id가 myprice 인 input 태그에서 값을 가져온다.
let myprice = $('#myprice').val();
// 2. 만약 값을 입력하지 않았으면 alert를 띄우고 중단한다.
if (myprice == '') {
alert('올바른 가격을 입력해주세요');
return;
}
// 3. PUT /api/product/${targetId} 에 data를 전달한다.
$.ajax({
type: "PUT",
url: `/api/products/${targetId}`,
contentType: "application/json",
data: JSON.stringify({myprice: myprice}),
success: function (response) {
// 4. 모달을 종료한다. $('#container').removeClass('active');
$('#container').removeClass('active');
// 5. 성공적으로 등록되었음을 알리는 alert를 띄운다.
alert('성공적으로 등록되었습니다.');
// 6. 창을 새로고침한다. window.location.reload();
window.location.reload();
}
})
}
Controller
1) 클라이언트의 요청을 받음.
2) 요청에 따른 처리는Service
에게 맡김.
3) 클라이언트에게 응답.
Service
1) 사용자의 요구에 따른 비즈니스 로직 실행!
2) DB정보가 필요할 때 Repository에게 요청
- 전체 적인 흐름
package com.sparta.myselectshop.controller;
import com.sparta.myselectshop.dto.ProductMypriceRequestDto;
import com.sparta.myselectshop.dto.ProductRequestDto;
import com.sparta.myselectshop.dto.ProductResponseDto;
import com.sparta.myselectshop.service.ProductService;
import org.springframework.web.bind.annotation.*;
import java.sql.SQLException;
import java.util.List;
@RestController
@RequestMapping("/api")
public class ProductController {
// 관심 상품 등록하기
@PostMapping("/products")
public ProductResponseDto createProduct(@RequestBody ProductRequestDto requestDto) throws SQLException {
ProductService productService = new ProductService();
// 응답 보내기
return productService.createProduct(requestDto);
}
// 관심 상품 조회하기
@GetMapping("/products")
public List<ProductResponseDto> getProducts() throws SQLException {
ProductService productService = new ProductService();
// 응답 보내기
return productService.getProducts();
}
// 관심 상품 최저가 등록하기
@PutMapping("/products/{id}")
public Long updateProduct(@PathVariable Long id, @RequestBody ProductMypriceRequestDto requestDto) throws SQLException {
ProductService productService = new ProductService();
// 응답 보내기 (업데이트된 상품 id)
return productService.updateProduct(id, requestDto);
}
}
package com.sparta.myselectshop.service;
import com.sparta.myselectshop.dto.ProductMypriceRequestDto;
import com.sparta.myselectshop.dto.ProductRequestDto;
import com.sparta.myselectshop.dto.ProductResponseDto;
import com.sparta.myselectshop.entity.Product;
import com.sparta.myselectshop.repository.ProductRepository;
import org.springframework.stereotype.Component;
import java.sql.SQLException;
import java.util.List;
@Component
public class ProductService {
public ProductResponseDto createProduct(ProductRequestDto requestDto) throws SQLException {
// 요청받은 DTO 로 DB에 저장할 객체 만들기
Product product = new Product(requestDto);
ProductRepository productRepository = new ProductRepository();
return productRepository.createProduct(product);
}
public List<ProductResponseDto> getProducts() throws SQLException {
ProductRepository productRepository = new ProductRepository();
return productRepository.getProducts();
}
public Long updateProduct(Long id, ProductMypriceRequestDto requestDto) throws SQLException {
ProductRepository productRepository = new ProductRepository();
Product product = productRepository.getProduct(id);
if(product == null) {
throw new NullPointerException("해당 상품은 존재하지 않습니다.");
}
return productRepository.updateProduct(product.getId(), requestDto);
}
}
package com.sparta.myselectshop.repository;
import com.sparta.myselectshop.dto.ProductMypriceRequestDto;
import com.sparta.myselectshop.dto.ProductResponseDto;
import com.sparta.myselectshop.entity.Product;
import org.springframework.stereotype.Component;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
@Component
public class ProductRepository {
public ProductResponseDto createProduct(Product product) throws SQLException {
// DB 연결
Connection connection = DriverManager.getConnection("jdbc:h2:mem:db", "sa", "");
// DB Query 작성
PreparedStatement ps = connection.prepareStatement("select max(id) as id from product");
ResultSet rs = ps.executeQuery();
if (rs.next()) {
// product id 설정 = product 테이블의 마지막 id + 1
product.setId(rs.getLong("id") + 1);
} else {
throw new SQLException("product 테이블의 마지막 id 값을 찾아오지 못했습니다.");
}
ps = connection.prepareStatement("insert into product(id, title, image, link, lprice, myprice) values(?, ?, ?, ?, ?, ?)");
ps.setLong(1, product.getId());
ps.setString(2, product.getTitle());
ps.setString(3, product.getImage());
ps.setString(4, product.getLink());
ps.setInt(5, product.getLprice());
ps.setInt(6, product.getMyprice());
// DB Query 실행
ps.executeUpdate();
// DB 연결 해제
ps.close();
connection.close();
return new ProductResponseDto(product);
}
public List<ProductResponseDto> getProducts() throws SQLException {
List<ProductResponseDto> products = new ArrayList<>();
// DB 연결
Connection connection = DriverManager.getConnection("jdbc:h2:mem:db", "sa", "");
// DB Query 작성 및 실행
Statement stmt = connection.createStatement();
ResultSet rs = stmt.executeQuery("select * from product");
// DB Query 결과를 상품 객체 리스트로 변환
while (rs.next()) {
Product product = new Product();
product.setId(rs.getLong("id"));
product.setImage(rs.getString("image"));
product.setLink(rs.getString("link"));
product.setLprice(rs.getInt("lprice"));
product.setMyprice(rs.getInt("myprice"));
product.setTitle(rs.getString("title"));
products.add(new ProductResponseDto(product));
}
// DB 연결 해제
rs.close();
connection.close();
return products;
}
public Long updateProduct(Long id, ProductMypriceRequestDto requestDto) throws SQLException {
// DB 연결
Connection connection = DriverManager.getConnection("jdbc:h2:mem:db", "sa", "");
// DB Query 작성
PreparedStatement ps = connection.prepareStatement("update product set myprice = ? where id = ?");
ps.setInt(1, requestDto.getMyprice());
ps.setLong(2, id);
// DB Query 실행
ps.executeUpdate();
// DB 연결 해제
ps.close();
connection.close();
return null;
}
public Product getProduct(Long id) throws SQLException {
Product product = new Product();
// DB 연결
Connection connection = DriverManager.getConnection("jdbc:h2:mem:db", "sa", "");
// DB Query 작성
PreparedStatement ps = connection.prepareStatement("select * from product where id = ?");
ps.setLong(1, id);
// DB Query 실행
ResultSet rs = ps.executeQuery();
if (rs.next()) {
product.setId(rs.getLong("id"));
product.setImage(rs.getString("image"));
product.setLink(rs.getString("link"));
product.setLprice(rs.getInt("lprice"));
product.setMyprice(rs.getInt("myprice"));
product.setTitle(rs.getString("title"));
}
// DB 연결 해제
rs.close();
ps.close();
connection.close();
return product;
}
}
- 3개의 클레스 별 역할 분배가 완료 (
Controller
->Service
->Repository
)- 하지만
ProductService
,ProductRepository
객체가 중복으로New
된다.- 각 클레스 별로 필요한 클레스를 의존하여 결합되어 있다.
-> 이상황에서Repository1
이 있다고 가정하고,Controller 1~5
가 전부 하나의Repository1
을 의존하는데 변경 사항이 발생한다? -> 모든 것을 수정해야함...!!!
하나의 객체 생성은 한번만!
생성된 객체를 필요한 곳에서 재사용!
- 강한 결합 -> 느슨한 결합
- 이러한 객체의 생성과 재사용, 의존관계 주입을 스프링 프레임워크에서 해준다!
- 빈 (Bean): 스프링이 관리하는 객체
- 스프링 IoC 컨테이너: '빈'을 모아둔 통
@ Component
@Component
public class ProductService { ... }
- 스프링 서버가 뜰 때 스프링 Ioc에
Bean
저장!
// 1. ProductService 객체 생성
ProductService productService = new ProductService();
// 2. 스프링 IoC 컨테이너에 빈 (productService) 저장
// productService -> 스프링 IoC 컨테이너
Bean
아이콘 확인- 스프링 Ioc에서 관리할
Bean
이라는 표시이다.
- 제약 조건
@ComponetScan
의 하위 패키지에 존재해야Component
를 인식할 수 있으며, 우리가 Spring프로젝트를 생성할 때 default로 들어가 있는Application
에 포함되어 있다.
- Bean객체 등록하기
@Autowired
: 스프링에 의해DI(의존성 주입)
된다.
@Component
//멤버 변수 DI
public class ProductService {
@Autowired
private ProductRepository productRepository;
// ...
}
@Component
//함수 DI
public class ProductService {
private final ProductRepository productRepository;
@Autowired
public ProductService(ProductRepository productRepository) {
this.productRepository = productRepository;
}
// ...
}
Lombok
의@RequiredArgsConstructor
의 사용으로 생략가능!
@Component
public class ProductService {
private final ProductRepository productRepository;
@Autowired
public ProductService(ApplicationContext context) {
// 1.'빈' 이름으로 가져오기
ProductRepository productRepository = (ProductRepository) context.getBean("productRepository");
// 2.'빈' 클래스 형식으로 가져오기
// ProductRepository productRepository = context.getBean(ProductRepository.class);
this.productRepository = productRepository;
}
// ...
}
- 문자열로 구성된 리스트 strings와 정수 n이 주어진다.
- 주어진 정수 n 번째를 문자를 비교해서 오름차순으로 정렬한다.
- 같은 n번째 문자가 나올경우 사전적 오름차순으로 정렬한다.
- 입출력 예시
strings | n | return |
---|---|---|
"sun", "bed", "car"] | 1 | ["car", "bed", "sun"] |
["abce", "abcd", "cdx"] | 2 | ["abcd", "abce", "cdx"] |
import java.util.*;
class Solution {
// 문자열로 구성된 strings , 정수 n이 주어질 때,
// 각 문자열의 인덱스 n번째 글자를 오름 차순으로 정렬하려 한다.
// 확인
public String[] solution(String[] strings, int n) {
String [] res = strings;
Arrays.sort(res);
String compare = "";
int equalLength = 0;
for (int i =0 ;i<res.length;i++){
for(int j = i;j<res.length;j++){
if(res[i].charAt(n) > res[j].charAt(n)){
compare = res[j];
res[j] = res[i];
res[i] = compare;
}else if(res[i].charAt(n) == res[j].charAt(n)){
if(i==j){
continue;
}
if(res[i].length()<=res[j].length()){
equalLength = res[i].length();
}else{
equalLength = res[j].length();
}
for(int k=0;k<equalLength;k++){
if(res[i].charAt(k) > res[j].charAt(k)){
compare = res[j];
res[j] = res[i];
res[i] = compare;
break;
}else if(res[i].charAt(k) < res[j].charAt(k)){
break;
}
}
}
}
}
return res;
}
}
- 이 문제는 Test케이스에서 계속 뻑나서 쉬운 문제지만 엄청 오래 걸렸다..
- 만약 앞에 문자가 더 작은 경우 반복문
break;
를 해줘야 하는 경우와, 같은 문자열이 다른 인덱스로 주어질 경우의 예외를 생각하지 못하였고, 질문방에서 힌트를 얻은 끝에 혼자 코드를 작성할 수 있었다.