Developer Note/국비과정 수업내용 정리&저장

24년 12월 13일

DH_PARK 2024. 12. 18. 02:19

어제는 CORS 에러에 대해 알아보기 까지 했다.

이를 해결하기 위해서 백엔드에서 cors 설정을 해주어야 한다. 


리액트 npm install 이 안되는 에러

c드라이브에 옮겨서 해보려고 하는중에 npm start 를 하니까 무반응이어서

평소처럼 node modules 와 package.json lock 을 지우고 npm install 을 해서

다시 실행하는게 안되길래 왜그런가 보니 상위폴더 이름이 “React수업” 이어서

한글이 포함된 경로 안에 들어있어서 안되는 거였음.


유효성체크

package com.example.app.controller;

import com.example.app.domain.dto.MemoDto;
import com.example.app.domain.dto.PersonDto;
import jakarta.validation.Valid;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.annotation.*;

import java.util.LinkedHashMap;
import java.util.Map;

@RestController
@RequestMapping("/memo")
@Slf4j
@CrossOrigin(origins={"<http://localhost:3000>","<http://127.0.0.1:3000>"}) //허용할 오리진명 명시
public class MemoController {

    @PostMapping(value = "/add",consumes = MediaType.APPLICATION_JSON_VALUE,produces = MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity<Map<String,Object>> add(@RequestBody @Valid MemoDto memoDto, BindingResult bindingResult){
        Map<String,Object> map = new LinkedHashMap<>();

        log.info("GET /param/02..!"+memoDto);

        if(bindingResult.hasErrors()) {
//			log.info("데이터 유효성 체크 오류 : " + bindingResult.getFieldError("id").getDefaultMessage()  );
//			model.addAttribute("id",bindingResult.getFieldError("id").getDefaultMessage());
            for(FieldError error : bindingResult.getFieldErrors()) {
                log.info("Error Field : "+error.getField()+" Error Msg : "+error.getDefaultMessage());
                map.put(error.getField(),error.getDefaultMessage()); //리액트에 json 타입으로 넘겨야 해서 map 으로 put 해줌 , 에러 기본 메시지
            }
            return new ResponseEntity<>(map,HttpStatus.EXPECTATION_FAILED);
        }

        return new ResponseEntity<>(map,HttpStatus.OK); //처리 결과에 따라 리스폰스를 다르게 반환 , personDto 가 null 이라면

    }

}

사용할 controller 부분에 CrossOrigin 어노테이션을 붙여 사용하면 그 url 에서 이 컨트롤러를 사용할 수 있게 해준다.

여기서 에러가 발생해서 전달하면 프론트에서는 catch 에서 잡아냄

import { useState,useEffect } from "react";
import axios from "axios";

const Memo = ()=>{
    const [id,setId] = useState()
    const [writer,setWriter] = useState()
    const [text,setText] = useState()
    const [regdate,setRegdate] = useState()
    const [message,setMessage] = useState(null) 
    const handleSubmit = ()=>{
        console.log({"id":id,"writer":writer,"text":text,"regdate":regdate});
        //서버로 요청하기
        axios.post(`http://localhost:8095/memo/add`, //
            {"id":id,"writer":writer,"text":text,"regdate":regdate},
            {headers:{'Content-Type':'application/json'}}) //url을 전달 ? //백틱
            .then(resp=>{
                console.log(resp);
                const data = resp.data;
                setMessage(resp.data);  //이 response 메시지를 전달

            })
  
            .catch(err=>{console.log(err);
                setMessage(err.response.data) 
            })
    }
    return (
        <div>

            <h2>MEMO ADD</h2>
            <div>
                <label> ID : </label ><span>{message&& message.id}</span> <br />
                <input type="text" name="id" onChange={e=>{setId(e.target.value)}}/>
            </div>
            <div>
                <label> WRITER : </label><span>{message && message.writer}</span> <br />
                <input type="text" name="writer" onChange={e=>{setWriter(e.target.value)}}/>
            </div>
            <div>
                <label> TEXT : </label><span>{message && message.text}</span><br />
                <input type="text" name="text" onChange={e=>{setText(e.target.value)}}/>
            </div>
            <div>
                <label> REGDATE : </label><span>{message && message.regdate}</span> <br />
                <input type="datetime-local" name="regdate" onChange={e=>{setRegdate(e.target.value)}}/>
            </div>
            <div>
                <button onClick={handleSubmit}>전송</button>
            </div>

        </div>
    )
}

export default Memo;

리액트 스프링부트 연동해서 회원가입 해보기

import { useState,useEffect } from "react"
import axios from "axios"

const Join = ()=>{
    const [username,setUsername] = useState("")
    const [password,setPassword] = useState("")

    const handleJoin = ()=>{
        axios
        .post(
            "<http://localhost:8090/join>",
            {username,password},
            {headers:{"Content-Type":'application/json'} }
        )
        .then(resp=>{console.log(resp)})
        .catch(err=>{console.log(err);
            console.log(err.data);
                    
        })
    }

    return (
        <div>
        <h1>Join Page</h1>
        Username : <input type="text" name="username" onChange={e=>setUsername(e.target.value)}/><br />
        Password : <input type="password" name="password" onChange={e=>setPassword(e.target.value)}/><br />
        <button onClick={handleJoin}>회원가입</button>
        </div>
    )
}

export default Join

view 페이지에서 join 요청을 보낸다.

※ 주의할 점 : axios 에서 json 타입으로 값을 받아오고 전달해야 한다.


axios 코드를 작성할 때

axios
        .post(
            "<http://localhost:8090/join>",
            {username,password},
            {headers:{"Content-Type":'application/json'} }
        )
        .then(resp=>{console.log(resp)})
        .catch(err=>{console.log(err);
            console.log(err.data);

이렇게 input 으로 받는 username , password 를 굳이 json타입처럼 키 밸류 형태로 작성하지 않아도

데이터가 잘 전달이 된다.

 

{"username":username,"password":password},

그리고 이런식으로 정확히 명시를 해주어도 데이터가 당연하게 잘 전달이 된다…

 

왜 그런가 알아보니 자바스크립트에서는 리터럴 축약 표현 이라는게 있다고 한다.

그래서 굳이 key 값을 적지 않아도 알아서 매핑이 된다고 한다.

 

나는 이게 json 타입이라 어떤 값이 들어가는지 정확히 키 값을 명시해줘야 하는줄 알았는데 ,

그런게 아니고 정확히 아직은 모르겠지만 키 값은 그저 value 값을 알려주는 문자열일 뿐이고

     {"username":password,"password":username},

이런식으로 서로 데이터 순서를 바꿔서 넣으면 join 페이지에서 첫번째 input 에서 id 값을 적는다고 해도 어차피 처리가 되는건 백엔드쪽 코드에서 데이터가 save 되기 때문에 join페이지에서 두 번째 input 칸인 password 부분에서 넣는 데이터가 username 으로 들어가게 된다.

 

#그러니까 중요한 건 , 결국 데이터를 처리한다거나 실제 처리되는 로직이 일어나는 곳은 백엔드쪽 코드라는것이다.

이 때문에 프론트와 백엔드가 나뉘는 것이지.

 

마찬가지로 headers 부분도 특별히 Content-Type 을 지정해주지 않으면 자동으로 application/json 타입으로 명시가 된다고 한다.

그래서 특별히 헤더에 다른 요청값을 전달해야 하는 경우에만 headers 를 넣어서 전달을 하면 될 것 같다.

 


 

### CORS 에러에 대한 security 설정

   @Bean
    public CorsConfigurationSource corsConfigurationSource(){
        CorsConfiguration config = new CorsConfiguration();

        config.setAllowCredentials(true);
        config.setAllowedOrigins(List.of("<http://localhost:3000>"));
        config.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS"));
        config.setAllowedHeaders(List.of("*"));
        config.setExposedHeaders(List.of("*"));

        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", config);
        return source;
    }

CORS 설정해줘야 접근이 가능해진다.


 

'Developer Note > 국비과정 수업내용 정리&저장' 카테고리의 다른 글

24년 12월 16일  (1) 2024.12.18
24년 12월 12일  (0) 2024.12.18
24년 12월 10일  (0) 2024.12.18
24년 12월 9일  (1) 2024.12.18
24년 12월 6일  (2) 2024.12.15