React+Spring Boot

상품 상세 기능

작성자
vita
작성일
2023-11-19 22:02
조회
380

상품 상세 기능

상품 상세의 전반적인 흐름은 아래와 같습니다.

 

ListProduct.js 에서 http://localhost/detail/상품코드 형식으로 만들어진 하이퍼링크를 클릭합니다.

상품코드가 Controller, dao, mapper에 순차적으로 전달됩니다.  sql 명령어를 실행하여 상품코드에 해당하는 1개의 레코드를 Controller에 되돌려줍니다.

Controller는 그 자료를 json 형태로 리턴합니다.

React의 DetailProduct에서는 서버에서 받은 json 데이터를 변환하여 화면에 출력합니다.

 

1. ProductItem.js (React)

파일 위치: c:/react/frontend/src/components/ProductItem.js

import React from 'react';

import { Link } from 'react-router-dom';

import './main.css'

 

function ProductItem({ product_code, product_name, price, filename }) {

[생략]

    return (

      <div style={{ margin: '5px' }}>

        <span dangerouslySetInnerHTML={{ __html: img }}></span>

        <Link to={`/detail/${product_code}`}>

          상품명 : {product_name}<br />

          가격: {price}원

        </Link>

        <br /><br />

      </div>

    )

  }

}

export default ProductItem;

 

[해설]

 

        <Link to={`/detail/${product_code}`}>

          상품명 : {product_name}<br />

          가격: {price}원

        </Link>

 

상품정보를 클릭하면 상세페이지로 이동하기 위하여 하이퍼링크를 걸었습니다.

<Link> 태그의 형식은 아래와 같습니다.

 

<Link to=’이동할주소’>텍스트</Link>

 

이동할 주소는 /detail/${row.product_code} 입니다.

상품코드가 1번이라면 http://localhost/detail/1 로

url이 구성됩니다.

 

2. DetailProduct.js (React)

파일 위치: c:/react/frontend/src/components/DetailProduct.js

import React, { useRef, useEffect, useState } from 'react';

import './main.css';

import { useNavigate } from 'react-router-dom';

 

function useFetch(url) {

  const [data, setData] = useState(null);

  const [loading, setLoading] = useState(true);

 

  useEffect(() => {

    fetch(url)

      .then(response => {

        return response.json();

      })

      .then(data => {

        setData(data);

        setLoading(false);

      })

  }, []);

  return [data, loading];

}

 

function DetailProduct() {

  const paths = window.location.href.split('/');

  const url = '/' + paths[paths.length - 2] + '/' + paths[paths.length - 1];

  const [data, loading] = useFetch(url);

  const navigate = useNavigate();

  const product_name = useRef();

  const price = useRef();

  const description = useRef();

  const img = useRef();

 

  if (loading) {

    return (

      <div>loading</div>

    )

  } else {

    let src='';

    let image_url = '';

    if (data.filename !== '-') {

      src = `http://localhost/static/images/${data.filename}`;

      image_url = `<img src=${src} width='300px' height='300px' />`;

    } else {

      image_url='';

    }

    return (

      <>

        <table>

          <tbody>

            <tr>

              <td>상품명</td>

              <td><input ref={product_name} defaultValue={data.product_name} /></td>

            </tr>

            <tr>

              <td>가격</td>

              <td><input type='number' ref={price} defaultValue={data.price} /></td>

            </tr>

            <tr>

              <td>상품설명</td>

              <td><textarea rows='5' cols='60' ref={description} defaultValue={data.description} /></td>

            </tr>

            <tr>

              <td>상품이미지</td>

              <td>

                <span dangerouslySetInnerHTML={{ __html: image_url }}></span>

                <br />

                <input type='file' ref={img} />

              </td>

            </tr>

            <tr>

              <td colSpan='2' align='center'>

                <button onClick={() => navigate('/')}>목록</button>

              </td>

            </tr>

          </tbody>

        </table>

      </>

    );

  }

};

 

export default DetailProduct;

 

[해설]

 

상세 화면은 WriteProduct.js 와 많이 비슷합니다.

 

<td><input ref={product_name} defaultValue={data.product_name} /></td>

 

상품이름을 태그에 표시하기 위하여 defaultValue 속성을 추가했습니다.

 

    let src='';

    let image_url = '';

    if (data.filename !== '-') {

      src = `http://localhost/static/images/${data.filename}`;

      image_url = `<img src=${src} width='300px' height='300px' />`;

    } else {

      image_url='';

    }

    <span dangerouslySetInnerHTML={{ __html: image_url }}></span>

 

기존에 상품이미지가 업로드된 경우 그 상품이미지를 표시하기 위하여 조건문을 추가했습니다.

 

3. App.js (React)

App.js 에 DetailProduct 항목을 추가합니다.

 

파일 위치: c:/react/frontend/src/App.js

import React from 'react';

import { Routes, Route } from 'react-router';

import { BrowserRouter } from 'react-router-dom';

import ListProduct from './components/ListProduct';

import WriteProduct from './components/WriteProduct';

import DetailProduct from './components/DetailProduct';

function App() {

  console.warn = function no_console() { }; //경고문 제거

  return (

    <>

      <BrowserRouter>

        <Routes>

          <Route path='/' element={<ListProduct />} />

          <Route path='/write' element={<WriteProduct />} />

          <Route path='/detail/:product_code' element={<DetailProduct />} />

          <Route path='*' element={<ListProduct />} />

        </Routes>

      </BrowserRouter>

    </>

  );

}

export default App;

 

[해설]

 

<Route path='/detail/:product_code' element={<DetailProduct />} />

 

상품을 클릭하면 서버로 /detail/상품코드 형태로 호출이 됩니다.

:product_code 는 변수입니다.  DetailProduct.js 에 product_code 라는 변수명으로 상품코드가 전달됩니다.

 

4. product.xml (Spring Boot)

 

id가 detail인 태그를 추가합니다.

 

파일 위치: src/main/resources/mappers/product.xml

<?xml version="1.0" encoding="UTF-8"?>

[생략]

    <insert id="insert">

    insert into product (product_name, description, price, filename)

    values

    (#{product_name}, #{description}, #{price}, #{filename})

    </insert>

 

    <select id="detail" resultType="java.util.Map">

        select * from product

        where product_code=#{product_code}

    </select>

 

</mapper>    

 

[해설]

 

sql 명령어를 실행한 후 map에 자료를 저장하여 dao로 리턴합니다.  상품코드가 일치하는 1개의 레코드가 리턴됩니다.

 

    <select id="detail" resultType="java.util.Map">

        select * from product

        where product_code=#{product_code}

    </select>

 



 

5. ProductDAO.java (Spring Boot)

 

detail() method를 작성합니다.

 

파일위치: src/main/java/com/example/product/ProductDAO.java

package com.example.product;

 

import java.util.List;

import java.util.Map;

 

import org.apache.ibatis.session.SqlSession;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.stereotype.Repository;

 

@Repository

public class ProductDAO {

 

    @Autowired

    SqlSession sqlSession;

    public List<Map<String, Object>> list(String product_name) {

        return sqlSession.selectList("product.list", "%" + product_name + "%");

    }

 

    public void insert(Map<String, Object> map) {

        sqlSession.insert("product.insert", map);

    }

 

    public Map<String, Object> detail(String product_code) {

        return sqlSession.selectOne("product.detail", product_code);

    }

 

}

 

[해설]

 

    public Map<String, Object> detail(String product_code)

 

Controller에서 전달한 상품코드가 String product_code 변수에 저장됩니다.

 

 

return sqlSession.selectOne("product.detail", product_code);

 

mapper에 상품코드를 전달하여 레코드를 조회합니다.

상품코드는 primary key로 설정되어 있으므로 1개만 리턴됩니다.  레코드가 2개 이상이면 selectList(), 1개이면 selectOne()을 사용합니다.

 

 

 

6. ProductController.java (Spring Boot)

detail() method를 작성합니다.

 

파일위치: src/main/java/com/example/product/ProductController.java

[생략]

@RestController

public class ProductController {

[생략]

    @RequestMapping("/insert")

    public void insert(@RequestParam Map<String, Object> map, @Request HttpServletRequest request) {

[생략]

    }

    @RequestMapping("/detail/{product_code}")

    public Map<String, Object> detail(@PathVariable String product_code) {

        return productDao.detail(product_code);

    }

}

 

[해설]

 

@RequestMapping("/detail/{product_code}")

    public Map<String, Object> detail(@PathVariable String product_code) {

 

상세화면을 보기 위한 url의 형식을 아래와 같이 만들었습니다.

 

http://localhost/detail/{상품코드}

 

상품코드가 1번이라면 http://localhost/detail/1 이 됩니다.  

상품코드는 클릭할 때마다 바뀌므로 변수로 처리가 되어야 합니다. 이 변수는 url에 포함되는 변수이므로 @PathVariable 어노테이션을 붙여서 값을 전달했습니다.

 

        return productDao.detail(product_code);

 

상품코드에 해당하는 레코드를 dao에 요청합니다.

7. 상품 상세 실행 결과 확인

상품목록에서 상품정보 링크를 클릭하면 해당되는 상품의 상세 정보를 출력합니다.

 

Scroll to Top