프론트엔드는 리엑트, 백엔드는 스프링부트로 개발을 하고 있습니다.
회원가입 부분에서 아이디와 비밀번호를 적고, 회원가입 버튼을 누르려고 할 때, 오류가 납니다.
오류의 종류는
Access to XMLHttpRequest at 'http://localhost:8080/register' from origin 'http://localhost:3000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
입니다.
(데이터베이스는 postgresql을 쓰고 있습니다.)
다음은 코드입니다.
리엑트(회원가입 부분)
// RegisterPage.js
import React, { useState } from 'react';
import { Link } from 'react-router-dom';
import axios from 'axios';
//import axios from 'axios';
const RegisterPage = () => {
/*
axios를 사용해서 서버로 post 요청을 보내고, 사용자가 입력한 아이디와 비밀번호를 전송한다.
1.react 컴포넌트 생성
const RegisterPage = () => { ... }에서 RegisterPage는 함수형 컴포넌트를 정의한다.
이 컴포넌트는 회원가입 페이지를 나타낸다.
2.useState를 사용한 상태 관리
const [username, setUsername] = useState('');
와 const [password, setPassword] = useState('');는 React의 useState 훅을 사용하여 각각 아이디와 비밀번호의 상태를 관리한다.
3.폼 제출 핸들러 함수
const handleSubmit = async (e) => { ... }는 폼 제출을 처리하는 함수이다.
async 함수로 비동기 처리를 수행하며,
e.preventDefault()를 호출하여 기본 제출 동작을 막는다.
4.axios를 사용한 서버 통신
axios.post('http://localhost:8080/register', { username, password });
는 axios를 사용하여 서버에 POST 요청을 보낸다.
'http://localhost:8080/register'는 Spring Boot에서 정의한 회원가입을 처리하는 엔드포인트이다.
5.컴포넌트 내용 반환
return ( ... )에서는 JSX를 사용하여 실제로 화면에 표시되는 컴포넌트의 구조를 정의한다.
<form> 요소 안에 아이디와 비밀번호 입력 필드, 회원가입 버튼, 돌아가기 버튼이 있다.
6.이벤트 핸들러 함수와 상태 업데이트:
입력 필드에는 onChange 이벤트에 연결된 함수를 통해 입력 값을 상태에 업데이트한다.
예를 들어, onChange={(e) => setUsername(e.target.value)}는 아이디 입력 필드 값이 변경될 때마다 setUsername 함수를 호출하여 상태를 업데이트한다.
7.React Router의 Link 컴포넌트 활용:
<button><Link to="/">돌아가기</Link></button>에서는
React Router의 Link 컴포넌트를 사용하여 홈 페이지로 이동하는 링크를 만든다.
*/
const [username, setUsername] = useState('');
const [password, setPassword] = useState('');
const handleSubmit = async (e) => {
e.preventDefault();
try {
// 'api/register'만 적어야 세션과 쿠키가 넘어온다. proxy의 도메인이 cors를 해결해 주기 때문이다.
//api는 setupProxy 파일에서 구현한 부분이다.
const response = await axios.post('http://localhost:8080/register', { username, password });
console.log(response.data); // Optional: Log server response
} catch (error) {
if (error.response) {
// 서버가 응답한 상태 코드가 2xx가 아닌 경우
console.error('Server responded with an error:', error.response.data);
} else if (error.request) {
// 요청이 전송되었지만 응답이 없는 경우
console.error('No response received:', error.request);
} else {
// 오류를 발생시킨 요청을 설정하는 중에 문제가 발생한 경우
console.error('Error setting up the request:', error.message);
}
}
};
return (
<div>
<form onSubmit={handleSubmit}>
아이디:{' '} <input type="text" name="username" value={username} onChange={(e) => setUsername(e.target.value)} /><br />
비밀번호: {' '}<input type="password" name="password" value={password} onChange={(e) => setPassword(e.target.value)} /><br />
<input type="submit" value="회원가입" />
<button><Link to="/">돌아가기</Link></button>
</form>
</div>
);
};
export default RegisterPage;
백엔드
WebConfig.java
package com.example.demo;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/*
* 기본적으로 브라우저는 다른 출저에서 리소스를 로드하는 것을 막는다.
* 따라서 react 앱이 실행 중인 출저에서 스프링부트 서버의 출저로의 요청이 차단 될 수 있다.
* 그래서 springboot 어플에서 cors를 허용하는 설정을 추가해야 한다.
* */
@Configuration
public class WebConfig implements WebMvcConfigurer{
//cors를 백엔드에서 허용할 수 있도록 한다.
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/register")
.allowedOrigins("http://localhost:3000")
.allowCredentials(true)
.allowedMethods("GET", "POST", "PUT", "DELETE")
.allowedHeaders("*");
/*
* .exposedHeaders("Access-Control-Allow-Origin")를 추가하여
* 브라우저가 요청에 대한 응답을 허용하는 데 필요한 헤더를 알 수 있게 합니다.
* */
}
}
MyController.java
package com.example.demo;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import lombok.RequiredArgsConstructor;
//cors 설정을 직접 추가한다.
@Controller
@CrossOrigin(origins = "http://localhost:3000", allowCredentials = "true")
@RequiredArgsConstructor
public class MyController {
@Autowired //autowired를 통해 쉽게 객체를 불러온다.
private NeuralNetworkRepository neuralnetworkrepository;
private MemberService memberService;
//기본 페이지를 요청한다.
@GetMapping("/")
public String test() {
return "MainPage"; //Mainpage를 찾아간다.(우리는 리엑트와 연동했으므로 리엑트 파일을 찾아간다.)
}
@GetMapping("/login")
public String loginPage() {
return "LoginPage";
}
//회원가입 페이지를 요청한다.
@GetMapping("/register")
public String registerPage() {
return "RegisterPage";
}
@PostMapping("/register")
@CrossOrigin(origins = "http://localhost:3000", allowCredentials = "true")
//클라이언트에서 json 형식으로 데이터를 전송하고 있으므로, requestbody를 사용해야 한다.
public String save(@RequestBody Map<String, String> requestBody,
RedirectAttributes redirectAttributes) {
// Use memberService to handle registration
String username = requestBody.get("username");
String password = requestBody.get("password");
try {
// 중복된 아이디 확인
if (neuralnetworkrepository.existsByUsername(username)) {
redirectAttributes.addFlashAttribute("error", "이미 사용 중인 아이디입니다. 다른 아이디를 선택해주세요.");
return "redirect:/register";
}
// 아이디가 중복되지 않은 경우 저장
memberService.addNeuralNetwork(username, password);
// 회원가입이 성공한 경우 리다이렉션하면서 성공 메시지 전달
redirectAttributes.addFlashAttribute("success", "User registered successfully!");
return "redirect:/";
} catch (Exception e) {
// 예외 처리
redirectAttributes.addFlashAttribute("error", "회원가입 중 오류가 발생했습니다.");
return "redirect:/register";
}
}
@GetMapping("/result")
public String resultPage() {
return "ResultPage";
}
}
SecurityConfig.java
package com.example.demo;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
//SpringSecurity를 이용해서 보안 설정을 구현한 클라스이다.
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfiguration {
private final MemberService userDetailsService;
public SecurityConfig(MemberService userDetailsService) {
this.userDetailsService = userDetailsService;
}
//http 보안 설정을 정의한다.
//register는 모든 사용자들에게, 그 외 요청은 인증된 사용자들에게만.
protected void configure(HttpSecurity http) throws Exception {
http
.cors() // CORS 설정을 허용
.and()
.authorizeRequests()
.requestMatchers("/public/**").permitAll() // 특정 경로에 대한 권한 없이 허용
.anyRequest().authenticated() // 나머지 요청은 인증 필요
.and()
.formLogin()
.loginProcessingUrl("/login") // 로그인 처리 URL
.usernameParameter("username")
.passwordParameter("password")
.permitAll()
.and()
.logout()
.logoutUrl("/logout") // 로그아웃 처리 URL
.permitAll()
.and()
.httpBasic(); // 기본 HTTP 기반 인증 사용
}
@Bean
//bean의 이름을 변경하여 충돌을 피한다.
//비밀번호 암호화를 위한 메서드이다.
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
Person.java
package com.example.demo;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.Data;
//사용자 정보를 저장할 엔터티 클래스이다.
@Data
@Entity
@Table(name ="person")
public class Person {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private String username;
private String password;
public Person() {
}
public Person(String username, String password) {
this.username = username;
this.password = password;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
//GeneratedValue(strategy = GenerationType.IDENTITY)는 데이터베이스가 자동으로 아이디를 생성,할당하도록 한다.
//alt+shift+s에서 getter/setter를 만드는 옵션으로 getter,setter를 쉽게 생성한다.
}
추가내공 100 있습니다. 감사합니다.