Notice
Recent Posts
Recent Comments
Link
| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 5 | 6 | |
| 7 | 8 | 9 | 10 | 11 | 12 | 13 |
| 14 | 15 | 16 | 17 | 18 | 19 | 20 |
| 21 | 22 | 23 | 24 | 25 | 26 | 27 |
| 28 | 29 | 30 |
Tags
- JAVA객체지향
- Java데이터 타입
- 컴파일
- JAVA기초
- 메서드
- 접근제어지시자
- for문
- Thread
- break문
- 생성자
- 상수
- 인텔리제이 기초 설정
- this예약어
- 연관관계
- 집합관계
- function
- continue문
- OPP개념
- multi-threading
- java변수
- IntelliJ IDEA
- 메서드 오버로딩
- 형 변환
- 자바 멀티스레딩
- 시스템 환경 변수 편집
- While
- 포함관계
- Java
- 인텔리제이 한글 깨짐 해결법
- 반복문
Archives
- Today
- Total
최원종의 개발 블로그
V3-4 회원가입(사용자 등록과 JPA 영속성 활용) 본문

UserRepository 코드
중요 개념 및 핵심 요약
SRP (Single Responsibility Principle) 개념
단일 책임 원칙(SRP)은 객체지향 설계의 5대 원칙(SOLID) 중 첫 번째 원칙.
정의: 하나의 클래스는 단 하나의 책임(기능)만 가져야 한다.
판단 기준: 클래스를 수정해야 하는 이유는 오직 하나뿐이어야 한다.
목적: 코드의 응집도를 높이고 결합도를 낮추어,
변경 사항이 발생했을 때 다른 코드에 미치는 영향을 최소화하기 위함.
핵심 요약
적용된 책임 Persistence(영속성): 오직 데이터베이스와의 통신 및 데이터 입출력만 담당함.
분리된 책임 비즈니스 로직(Service), 화면 렌더링(Controller), 데이터 전송(DTO) 등은 포함하지 않음.
장점 만약 DB를 MySQL에서 Oracle로 바꾸거나 JPA 설정을 변경할 때, 오직 이 클래스만 수정하면 됨.
package com.tenco.blog.user;
import jakarta.persistence.EntityManager;
import jakarta.transaction.Transactional;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Repository;
// SRP - 단일 책임의 원칙
@Repository // IoC 대상
@RequiredArgsConstructor
public class UserRepository {
//DI - 스프링 프레임 워크가 주소값 자동 주입
private final EntityManager em;
// 회원 가입 요청시 --> INSERT
@Transactional
public User save(User user) {
// 매개 변수로 들어온 User Object 비영속 상태이다.
em.persist(user);
// 리턴시 User Object 는 영속화 된 상태이다.
return user;
}
// 사용자 이름 중복 확인
public User findByUsername(String username) {
String jpqlStr = """
SELECT u FROM User u WHERE u.username = :username
""";
// Query query = em.createQuery(jpqlStr, User.class);
// query.setParameter("username", username);
// User userEntity = (User) query.getSingleResult();
try {
return em.createQuery(jpqlStr, User.class)
.setParameter("username", username)
.getSingleResult();
} catch (Exception e) {
return null;
}
}
// 로그인 요청시 --> SELECT
public User findByUsernameAndPassword(String username, String password) {
String jpqlStr = """
SELECT u FROM User u WHERE u.username = :username AND u.password = :password
""";
try {
return em.createQuery(jpqlStr, User.class)
.setParameter("username", username)
.setParameter("password", password)
.getSingleResult();
} catch (Exception e) {
return null;
}
}
}
Usercontroller 코드
핵심 메서드 분석
1. 회원가입 페이지 요청(joinFormPage)
방식: GET 요청
역할:단순히 사용자에게 데이터를 입력받을 HTML 폼(user/join-form.mustache)을 보여줌
2. 회원가입 기능 수행 (joinProc)
방식: POST 요청 (데이터 생성 및 보안을 위해 사용)
DTO 활용: UserRequest.JoinDTO를 사용하여
클라이언트가 보낸 데이터를 한 번에 바인딩함. (파싱 전략 2)
로직 흐름
1. 유효성 검사 (validate): 입력값이 비어있거나 형식에 맞지 않는지 확인함.
2. 비즈니스 로직 (중복 검사): userRepository.findByUsername을 통해
이미 존재하는 아이디인지 체크함. 중복 시 예외를 던짐.
3. 영속화 (save): DTO를 엔티티로 변환(toEntity)하여 DB에 저장함.
4. 응답 (redirect): 성공 시 메인 페이지(/)로 사용자를 보냄.
package com.tenco.blog.user;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
@Slf4j
@Controller // IoC
@RequiredArgsConstructor // DI 처리
public class UserController {
private final UserRepository userRepository;
// 회원 가입 화면 요청
// 주소 설계 - http://localhost:8080/join-form
@GetMapping("/join-form")
public String joinFormPage() {
return "user/join-form";
}
// 회원 가입 기능 요청
// 주소 설계 - http://localhost:8080/join
// 파싱 전략 1 - key=value 구조 (@RequestParam 사용)
// 파싱 전략 2 - Object DTO 설계
@PostMapping("/join")
public String joinProc(UserRequest.JoinDTO joinDTO) {
log.info("username " + joinDTO.getUsername());
log.info("password " + joinDTO.getPassword());
log.info("email " + joinDTO.getEmail());
// 1. 유효성 검사 하기
joinDTO.validate(); // 유효성 검사 ---> 오류 --> 예외 처리 넘어감
// 회원가입 요청 전 ==> 중복 username 검사
User userCheckName = userRepository.findByUsername(joinDTO.getUsername());
if(userCheckName != null) {
throw new IllegalArgumentException("이미 사용중인 username 입니다 : "
+ userCheckName.getUsername());
}
userRepository.save(joinDTO.toEntity());
// TODO
// 로그인 화면으로 리다이렉트 처리 예정
return "redirect:/";
}
}
UserRequest 코드
핵심 기능 정리
1. 정적 내부 클래스 활용 (static class JoinDTO)
UserRequest라는 큰 바구니 안에 JoinDTO, LoginDTO 등 목적에 맞는 작은 바구니들을 모아서 관리함.
관련 있는 DTO들을 한 클래스에서 관리하여 구조가 깔끔해짐.
2. 엔티티 변환 메서드 (toEntity)
역할: DTO에 담긴 데이터를 바탕으로 실제 DB에 저장될 User 엔티티 객체를 생성.
장점: 컨트롤러에서 복잡하게 new User(...)를 호출할 필요 없이,joinDTO.toEntity() 한 줄로 깔끔하게 엔티티화가능.
이때 이전에 구현한 빌더 패턴을 활용하여 안전하게 객체를 생성.
3. 자체 유효성 검사 (validate)
로직: null 체크, 공백 체크(isEmpty), 그리고 이메일 형식(@ 포함 여부) 등을 검증.
효과: 잘못된 데이터가 서비스 레이어나 DB까지 흘러 들어가지 않도록 입구(DTO)에서 원천 차단.
문제가 있을 경우 IllegalArgumentException을 던져 예외 처리를 유도.
package com.tenco.blog.user;
import lombok.Data;
public class UserRequest {
// 회원가입 DTO
@Data
public static class JoinDTO {
private String username;
private String password;
private String email;
// 편의 기능 추가 - 내가 가지고 있는 멤버 변수에 값으로 User 엔티를 생성
public User toEntity() {
return User.builder()
.username(username)
.password(password)
.email(email)
.build();
}
// 유효성 검사 메서드 만들기
public void validate() {
if (username == null || username.trim().isEmpty()) {
throw new IllegalArgumentException("사용자명은 필수 입니다");
}
if(password == null || password.trim().isEmpty()) {
throw new IllegalArgumentException("비밀번호는 필수 입니다");
}
if(email == null || email.trim().isEmpty()) {
throw new IllegalArgumentException("이메일은 필수 입니다");
}
// 입력값 : abc@naver.com --> contains() --> true --> ! --> false
if(email.contains("@") == false) {
throw new IllegalArgumentException("올바른 이메일 형식이 아닙니다");
}
}
}
}
팁!
DTO를 따로 만드는 이유
Entity (User)
역할 : DB 테이블과 직접 매핑되는 설계도
수정 빈도: DB 구조가 바뀌지 않는 한 거의 없음
민감 정보: 비밀번호, 생성일 등 모든 정보 포함
DTO (JoinDTO)
역할 : 화면(UI)에서 넘어오는 데이터를 받는 바구니
수정 빈도 : 화면 디자인이나 기획이 바뀌면 자주 변경됨
민감 정보 : 필요한 정보(ID, PW, Email)만 선택적으로 포함
join-form.mustache 코드
{{> layout/header}}
<div class="container p-5 flex-grow-1">
<div class="card">
<div class="card-header"><b>회원가입을해주세요</b></div>
<div class="card-body">
<form action="/join" method="post">
<div class="mb-3">
<input type="text" class="form-control" placeholder="enter username" name="username">
</div>
<div class="mb-3">
<input type="password" class="form-control" placeholder="enter password" name="password">
</div>
<div class="mb-3">
<input type="email" class="form-control" placeholder="enter email" name="email">
</div>
<button type="submit" class="btn btn-primary form-control">회원가입</button>
</form>
</div>
</div>
</div>
{{> layout/footer}}
'Spring boot 입문' 카테고리의 다른 글
| V3-6 연관 관계 및 N + 1 문제 확인 (0) | 2026.05.13 |
|---|---|
| V3-5 로그인& 로그아웃(세션 기반 사용자 인증) (1) | 2026.05.12 |
| V3-3 게시글 목록보기(연산관계로 작성자 정보 표시) (0) | 2026.05.11 |
| V3-2 게시글 상세보기 (연관관계 기본 활용) (0) | 2026.05.11 |
| V3 (사용자 관리 및 연관 관계 설정) -1 연관관계 설정하기 (0) | 2026.05.11 |
