어제는 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 |