Jun's Blog

홈페이지 만들어보기[유효성체크](with SpringBoot) - (4) 본문

WEB/React

홈페이지 만들어보기[유효성체크](with SpringBoot) - (4)

luckydadit 2025. 2. 18. 15:08
1. 유효성(vaildation) 추가하기
pom.xml에 추가한 부분(with. IntelliJ)
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-validation</artifactId>
</dependency>

 

위의 내용 추가 후, 아래의 갱신 작업을 수행

 

member.java에 추가 및 수정한 부분(with. IntelliJ)
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Pattern;
import jakarta.validation.constraints.Size;
@NotBlank(message = "이름은 필수 입력 사항입니다.")
private String name;
@Column(unique = true, nullable = false)
@NotBlank(message = "이메일은 필수 입력 사항입니다.")
@Email(message = "올바른 이메일 형식으로 입력해 주셔야 합니다.")
private String email;
@NotBlank(message = "비밀번호는 필수 입력 사항입니다.")
@Size(min = 8, max = 16, message = "비밀번호는 8자리 이상, 16자리 이하로 입력해 주세요.")
@Pattern(regexp = ".*[A-Z].*", message = "비밀번호는 대문자 1개 이상을 포함해야 합니다.")
@Pattern(regexp = ".*[!@#$%].*", message = "비밀번호는 특수 문자 '!@#$%' 중 하나 이상을 포함해야 합니다.")
private String password;

 

memberController.java에 추가 및 수정한 부분(with. IntelliJ)
import jakarta.validation.Valid;
@PostMapping("/signup")
public ResponseEntity<?> signup(@Valid @RequestBody Member member, BindingResult bindingResult){
    // ResponseEntity : HTTP 응답 코드
    // @RequestBody : JSON 형태의 문자열을 자바의 객체 형식으로 변환해 줍니다.
    // @Valid : 유효성 검사를 수행하는 어노테이션입니다.
    // BindingResult : 유효성 검사시 문제가 있으면 예외를 발생시켜 줍니다.

 

import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
if(bindingResult.hasErrors()){
    Map<String, String> errors = new HashMap<String, String>();

    for(FieldError err : bindingResult.getFieldErrors()){
        errors.put(err.getField(), err.getDefaultMessage());
    }
    // HttpStatus.BAD_REQUEST : 사용자가 잘못된 형식의 요청이 들어오는 경우에 많이 사용합니다.
    return new ResponseEntity<>(errors, HttpStatus.BAD_REQUEST);
}

 

유효성 에러 확인용 메세지 출력 설정
System.out.println("유효성 오류 갯수");
System.out.println(bindingResult.getFieldErrorCount());
Map<String, String> errors = new HashMap<String, String>();

for(FieldError err : bindingResult.getFieldErrors()){
    errors.put(err.getField(), err.getDefaultMessage());
}
System.out.println(errors);

 

<유효성 검사 관련 실행 결과 - (1)>

테스트를 위해 React 부분의 Name에 대한  required 속성을 빼고 테스트합니다.
                <Form.Group className="mb-3 d-flex align-items-center" controlId="forName">
                    <Form.Label className="me-3 d-flex align-items-center" style={{ whiteSpace: "nowrap" }}>이름</Form.Label>
                    <Form.Control
                        type="text"
                        placeholder="이름을(를) 입력해 주세요."
                        value={name}
                        onChange={(event) => {setName(event.target.value)}}
                        required
                    />

 

<테스트 결과> - 400 Error 발생

 

<콘솔창 Error 메세지> - 이름 정보를 미입력하여 해당 Validation 으로 인한 오류로 확인

 

memberController.java에 추가 및 수정한 부분(with. IntelliJ)
    const [errors, setErrors] = useState({
        name: "", email: "", password: "", address: "", general: ""
    });
        }catch(error){
            if(error.response && error.response.data){
                setErrors(error.response.data); // 서버에서 받은 오류 메시지를 객체로 저장
            }else{
                // 다른 오류 메시지
                setErrors(prevErrors => ({ ...prevErrors, general: "회원 가입 중 오류가 발생하였습니다." }));
            }

        }
            {/* 일반 오류 메시지 표시 (예: 서버 오류) */}
            {errors.general && <Alert variant="danger">{errors.general}</Alert>}
                        isInvalid={!!errors.name} // 오류가 있으면 빨간색 표시
                    <Form.Control.Feedback type="invalid">
                        {errors.name}
                    </Form.Control.Feedback>
                        isInvalid={!!errors.email}
                    <Form.Control.Feedback type="invalid">
                        {errors.email}
                    </Form.Control.Feedback>
                        isInvalid={!!errors.password}
                    <Form.Control.Feedback type="invalid">
                        {errors.password}
                    </Form.Control.Feedback>
                        isInvalid={!!errors.address}
                    <Form.Control.Feedback type="invalid">
                        {errors.address}
                     </Form.Control.Feedback>

 

memberController.java에 추가 및 수정완료한 전체 소스 내용(with. IntelliJ)
import { useState } from "react";
import { Alert, Button, Container, Form } from "react-bootstrap";

import axios from "axios";

// 특정한 페이지로 이동을 시킬 때 사용하는 hook
import { useNavigate } from "react-router-dom";


function App(){
    console.log('회원 가입');
    // 파라미터 관련 state 변수 선언
    const [name, setName] = useState('');
    const [email, setEmail] = useState('');
    const [password, setPassword] = useState('');
    const [address, setAddress] = useState('');

    const navigate = useNavigate();

    // 예외 관련 state 변수 선언
    // 오류 메시지 객체
    const [errors, setErrors] = useState({
        name: "", email: "", password: "", address: "", general: ""
    });

    const handleSingup = async (event) => {
        event.preventDefault();
        /* spring boot에게 post 방식으로 전달 */
        try{
            /* response는 응답 객체 */
            const response = await axios.post(
                "http://localhost:9000/member/signup",
                {name, email, password, address}
            );

            // http 응답 코드 201은 요청 성공이고, 새로운 리소스 생성시 서버가 반환해주는 코드
            if(response.status === 201){
                alert('회원 가입 성공');
               
                navigate('/member/login'); // 로그인 페이지로 이동
            }
        }catch(error){
            if(error.response && error.response.data){
                setErrors(error.response.data); // 서버에서 받은 오류 메시지를 객체로 저장
            }else{
                // 다른 오류 메시지
                setErrors(prevErrors => ({ ...prevErrors, general: "회원 가입 중 오류가 발생하였습니다." }));
            }

        }
    };


    return(
        <Container className="mt-5">
            <h2 className="text-center mb-4">회원 가입</h2>

            {/* 일반 오류 메시지 표시 (예: 서버 오류) */}
            {errors.general && <Alert variant="danger">{errors.general}</Alert>}

            <Form onSubmit={handleSingup}>
                <Form.Group className="mb-3 d-flex align-items-center" controlId="forName">
                    <Form.Label className="me-3 d-flex align-items-center" style={{ whiteSpace: "nowrap" }}>이름</Form.Label>
                    <Form.Control
                        type="text"
                        placeholder="이름을(를) 입력해 주세요."
                        value={name}
                        onChange={(event) => {setName(event.target.value)}}
                        isInvalid={!!errors.name} // 오류가 있으면 빨간색 표시
                    />
                    <Form.Control.Feedback type="invalid">
                        {errors.name}
                    </Form.Control.Feedback>
                </Form.Group>
                <Form.Group className="mb-3 d-flex align-items-center" controlId="forEmail">
                    <Form.Label className="me-3 d-flex align-items-center" style={{ whiteSpace: "nowrap" }}>이메일</Form.Label>
                    <Form.Control
                        type="text"
                        placeholder="이메일을(를) 입력해 주세요."
                        value={email}
                        onChange={(event) => {setEmail(event.target.value)}}
                        isInvalid={!!errors.email}
                        required
                    />
                    <Form.Control.Feedback type="invalid">{errors.email}</Form.Control.Feedback>
                </Form.Group>  
                <Form.Group className="mb-3 d-flex align-items-center" controlId="forPassword">
                    <Form.Label className="me-3 d-flex align-items-center" style={{ whiteSpace: "nowrap" }}>비밀 번호</Form.Label>
                    <Form.Control
                        type="password"
                        placeholder="비밀 번호을(를) 입력해 주세요."
                        value={password}
                        onChange={(event) => {setPassword(event.target.value)}}
                        isInvalid={!!errors.password}
                        required
                    />
                    <Form.Control.Feedback type="invalid">{errors.password}</Form.Control.Feedback>
                </Form.Group>  
                <Form.Group className="mb-3 d-flex align-items-center" controlId="forAddress">
                    <Form.Label className="me-3 d-flex align-items-center" style={{ whiteSpace: "nowrap" }}>주소</Form.Label>
                    <Form.Control
                        type="text"
                        placeholder="주소을(를) 입력해 주세요."
                        value={address}
                        onChange={(event) => {setAddress(event.target.value)}}
                        isInvalid={!!errors.address}
                        required
                    />
                <Form.Control.Feedback type="invalid">{errors.address}</Form.Control.Feedback>
                </Form.Group>
                <Button variant="primary" type="submit" className="w-100">
                    회원 가입
                </Button>                                            
            </Form>
        </Container>
    );
}

export default App ;

 

<유효성 검사 관련 실행 결과 - (2)>

 

<테스트 결과> - 400 Error 발생


<콘솔창 Error 메세지> - 이름 정보를 미입력하여 해당 Validation 으로 인한 오류로 확인