React

imsapi 프로젝트 - imsfront 리액트

bumkee25 2023. 4. 20. 21:57

이클립스의 imsapi프로젝트와 리액트의 ims front 연결하기

1. 서로 다른 사이트간의 통신

  1.  imsapi -> http://localhost:9001
  2.  ims front -> http://localhost:3000

 - imsapi 프로젝트의 기본패키지에 WebMvcConfigurer를 상속하는 클래스 생성

@Configuration
public class WebMvcConfiguerImpl implements WebMvcConfigurer {

	@Override
	public void addCorsMappings(CorsRegistry registry) {
		registry.addMapping("/**")
		.allowedOrigins("http://localhost:3000")
		.allowedMethods("GET", "POST", "PUT", "DELETE")
		.allowedHeaders("*")
		.allowCredentials(true) //인증정보 허용
		.maxAge(3600); //초 단위고 3600초 1시간동안 열어두겠다는 뜻
	}
}

 

2. App.js

 - Route 사용방법: 터미널을 열고 react-router-dom 설치

    - npm install react-router-dom 입력 후 엔터

function App() {

  return (
    <BrowserRouter>
    <div className="App">
      <Routes>
        <Route path="/" Component={ItemList}/>
        <Route path="/detail/:id" Component={ItemDetail}/>
        <Route path="/update/:id" Component={ItemUpdate}/>
        <Route path="/create" Component={ItemCreate}/>
        <Route path="/*" Component={EmptyPage}/>
      </Routes>
    </div>
    </BrowserRouter>
  );
}

export default App; // 이하 생략

 

3. EmptyPage.js

import React from 'react' // 이하 import는 생략

function EmptyPage() {
  return (
    <div>
        지원하지 않는 서비스입니다.
    </div>
  )
}

 

4. ItemList.js

 - 아이템 목록 가져오기

function ItemList() {
    const [items, setItems] = useState([]);

    function getItemAll() {
        let url = "http://localhost:9001/item/all";
        let options = {
            method: "GET",
            headers: {
                "Content-Type": "application/json",
            },
        };

        fetch(url, options)
            .then((res) => {
                if (!res.ok) {
                    alert("실패");
                    throw new Error("자료를 못 가져왔음.");
                }
                return res.json();
            })
            .then((data) => {
                setItems(data.list)
            })
    }

    useEffect(getItemAll, [])

    return (
        <div>
            {
                items.length > 0 &&
                items.map(item =>
                    <ItemComp key={item.id} item={item} />)
            }
        </div>
    )
}

 -  useEffect : 컴포넌트가 처음 렌더링 될 때, 업데이트로 렌더링 될 때, 종료될 때 실행되는 기능

    - 구조 : useEffect(콜백함수명, 디펜던시 어레이)

  1. useEffect(콜백함수, );-> 해당 컴포넌트가 렌더링 될 때마다 콜백함수가 실행됨.
  2. useEffect(콜백함수, [ ]);-> 해당 컴포넌트가 처음 렌더링 될 때만 콜백함수가 실행됨. 딱 한 번만
  3. useEffect(콜백함수, [a, b]);-> a 또는 b에 의해서 해당 컴포넌트가 렌더링 될 때마다 콜백함수가 실행됨.

5. ItemComp.js

 - 아이템목록에 표시되는 형태

function ItemComp(props) {
  return (
    <div>
        <div className="item">
            <p>{props.item.id}</p>
            <p>
              <Link to={`detail/${props.item.id}`}>
                {props.item.itemName}
              </Link>
            </p>
            <p>{props.item.price}</p>
            <p>{props.item.discount}</p>
            <p>{props.item.price * (100 - props.item.discount) / 100}</p>
            <p>{props.item.updateDate}</p>
        </div>
    </div>
  )
}

 

6. ItemDetail.js

 - 아이템 자세히 보기, 아이템 삭제

function ItemDetail() {
    const [item, setItem] = useState(null)
    const [id] = useState(useParams().id)
    const navigate = useNavigate()

    function getItemById() {
        let url = `http://localhost:9001/item/id/${id}`;
        let options = {
            method: "GET",
            headers: {
                "Content-Type": "application/json",
            },
        };

        fetch(url, options)
            .then((res) => {
                if (!res.ok) {
                    alert("실패");
                    throw new Error("자료를 못 가져왔음.");
                }
                return res.json();
            })
            .then((data) => {
                setItem(data.dto)
            })
    }

    useEffect(getItemById, [id])

    function processDeleteFn() { // 삭제 이벤트 함수
        let yes = window.confirm("정말로 삭제할래요?")

        if(yes){
            let url = "http://localhost:9001/item"
            let options = {
                method: "DELETE",
                headers:{
                    "Content-Type" : "application/json"
                },
                body:JSON.stringify({
                    id:id
                })
            }

            fetch(url, options)
            .then(res =>{
                if(!res.ok){
                    alert("삭제 실패")
                    throw new Error("삭제 중 에러 발생")
                }
                return res.json();
            })
            .then(data =>{
                // window.location.assign("/")
                navigate("/")
            });
        }
    }

  return (
    <div>
        <h2>아이템 상세히 보기</h2>
        {
        item !== null && 
        <><div className="item">
            <p>{item.id}</p>
            <p>{item.itemName}</p>
            <p>{item.price}</p>
            <p>{item.discount}</p>
            <p>{item.salePrice}</p>
            <p>{item.itemDescribe}</p>
            <p>{item.staff}</p>
            <p>{moment(item.updateDate).format("YYYY-MM-DD HH:mm:ss")}</p>
            </div>
        <Link to={"/"}>HOME</Link> |
        <Link to={ `/update/${item.id}` }>수정</Link> |
        <Link onClick={processDeleteFn}>삭제</Link> | 
        <Link to={"/create"}>등록</Link>
        </>
    }   
    </div>
  )
}

 

7. ItemCreate.js

 - 아이템 생성

 1) form태그 안에 있는 input태그의 값 가져오기

 - useRef사용

    - 렌더링을 유발하지 않는 훅.

    - 사용방법 : const idRef = useRef( );

                        <input ref={idRef} />

 2) useRef에 데이터를 저장하면, current라는 속성명으로 값이 저장됨.

    따라서 useRef에 저장된 값을 사용하려면 아래와 같이 사용

                      let id = idRef.current.value;

 3) 화면에 들어왔을 때, 첫 번째 input태그에서 커서가 깜빡거리게 하기 위해 useRef와 useEffect 훅 사용

function ItemCreate() {
    const navigate = useNavigate(); // 등록 성공하면, item 자세히 보기로 가게 하기 위해서 등록
    const [staff] = useState("m1000");

    //시작:  useRef 등록 시작
    const itemNameRef = useRef();
    const priceRef = useRef();
    const discountRef = useRef();
    const eaRef = useRef();
    const itemDescribeRef = useRef();
    //끝:  useRef 등록 끝

    //시작:  아이템 등록 시작
    function onSubmitHandler(event) {
        event.preventDefault();

        // 각각의 input 태그에 입력된 값 가져오기
        let itemName = itemNameRef.current.value
        let price = priceRef.current.value;
        let discount = discountRef.current.value;
        let ea = eaRef.current.value;
        let itemDescribe = itemDescribeRef.current.value;

        let url = "http://localhost:9001/item"
        let options = {
            method: "POST",
            headers: {
                "Content-Type": "application/json"
            },
            body: JSON.stringify({//속성명과 속성값이 들어 있는 변수명이 같으면 이렇게 사용 가능
                itemName, 
                price,
                discount,
                ea,
                itemDescribe,
                staff
            })
        }

        fetch(url, options)
            .then(res => {
                if (!res.ok) {
                    alert("등록 실패");
                    throw new Error("등록 중 에러가 발생했습니다.");
                }
                return res.json();
            })
            .then(data => {
                alert("입력 성공");
                // window.location.href="/detail/"+data.dto.id;
                navigate(`/detail/${data.dto.id}`)// 등록 성공하면 아이템 자세히 보기로 이동
            })
    }
    //끝:  아이템 등록 끝

	// 커서 깜빡거리는 코드
    useEffect(() => { 
        itemNameRef.current.focus();
    }, [])
    
    return (
        <div>
            <h2>아이템 등록</h2>
            <form action='#' onSubmit={onSubmitHandler}>
                itemName: <input ref={itemNameRef} /><br />
                price: <input type='number' ref={priceRef} /><br />
                discount: <input type='number' ref={discountRef} /><br />
                ea: <input ref={eaRef} /><br />
                itemDescribe: <input ref={itemDescribeRef} /><br />
                staff: <input value={staff} readOnly /><br />
                <button>등록</button>
            </form>
        </div>
    )
}

 

8. ItemUpdate.js

 - 아이템 수정

 - onSubmitHandler : 수정된 값을 db에 저장하는 함수

   (다음에 formData 객체를 이용해서도 해봐야 함 -> 첨부파일 전송 가능)

 - onInputHandler : input창에 뭔가를 입력할 때마다 호출되는 콜백함수

   - formData와 다른점

     : value값을 넣고 oninput핸들러를 만들어주면, 원래 저장된 값(value)가 그대로 출력돼있다.

       따라서 아이템 정보 수정과 같은것들은 이런 방식으로 하고,

       비밀번호 수정과 같은 것들은 formData를 활용한다.

   - 리액트에서는 input태그에 value속성에 값을 입력하면, 어떤 값도 입력되지 않는다.

     따라서 값을 입력할 수 있도록 onInput이벤트를 걸어줬고=(이벤트가 발생할 때마다 그 값이 변하도록 state관리),

     그에 대한 콜백함수로 onInputHandler함수를 지정했다.

        let name = event.target.name; // 어떤 속성이 바뀌었는지 알기 위한 속성명 획득 목적의 코드
        let val = event.target.value; // input태그에 입력된 값을 받아 옴.

        // 기존 item 객체의 정보를 복사해서 newItem 객체에 넣고

        // 속성명이 담긴 name 변수와 바뀐 속성값이 담긴 val을 이용해서

        // newItem의 특정 속성을 변경한다.

        // 주의사항: 속성명 입력 시, [ ](대괄호)에 넣어야 인식됨.

        let newItem = {...item,[name]:val};
        setItem(newItem);

function ItemUpdate() {
    const navigate = useNavigate();
    const [item, setItem] = useState(null);
    const [id] = useState(useParams().id);

    function getItemById() {
        let url = `http://localhost:9001/item/id/${id}`;
        let options = {
            method: "GET",
            headers: {
                "Content-Type": "application/json",
            },
        };

        fetch(url, options)
            .then((res) => {
                if (!res.ok) {
                    alert("실패");
                    throw new Error("자료를 못 가져왔음.");
                }
                return res.json();
            })
            .then((data) => {
                setItem(data.dto)
            })
    }

    useEffect(getItemById,[id]);
	
    // 수정된 값을 db에 저장하는 코드
    function onSubmitHandler(e){
        e.preventDefault();

        let url = "http://localhost:9001/item"
        let options = {
            method : "PUT",
            headers : {
                "Content-Type" : "application/json"
            },
            body : JSON.stringify({
                id: item.id,
                itemName: item.itemName,
                price: item.price,
                discount: item.discount,
                ea: item.ea,
                itemDescribe: item.itemDescribe,
                staff: item.staff
            })
        }
        fetch(url, options)
        .then(res=>{
            if(!res.ok){
                alert("수정 실패")
                throw new Error("에러 발생")
            }
            return res.json();
        })
        .then(data=>{
            alert("수정 성공");
            navigate(`/detail/${id}`);
        })
        .catch(error=>{
            console.log(error.message);
        })
    }

    function onInputHandler(event){
        let name = event.target.name; 
        let val = event.target.value; 
        let newItem = {...item,[name]:val};
        setItem(newItem);
    }

  return (
    <div>
        <h2>아이템 수정</h2>
        {
        item !== null && 
        <form action='#' onSubmit={onSubmitHandler}>
            itemName : <input onInput={onInputHandler} name='itemName' value={item.itemName} /><br/>
            price : <input onInput={onInputHandler} name='price' value={item.price} /><br/>
            discount : <input onInput={onInputHandler} name='discount' value={item.discount} /><br/>
            salePrice : <input value={item.price*(100-item.discount)/100} disabled /><br/>
            ea : <input onInput={onInputHandler} name='ea' value={item.ea} /><br/>
            itemDescribe : <input onInput={onInputHandler} name='itemDescribe' value={item.itemDescribe} /><br/>
            <button>수정 완료</button>
        </form>
        }
    </div>
  )
}