졸프에서 현재 백엔드를 맡고 있기에 그 과정을 써보겠습니다!
| 구성 요소 | 사용 툴/기술 | 설명 |
|---|---|---|
| 백엔드 프레임워크 | Spring Boot | Java 기반 백엔드 프레임워크 |
| 빌드 도구 | Gradle | 의존성 관리 (여기선 Gradle 기준) |
| 개발 환경 | IntelliJ IDEA | Java 코딩용 IDE |
| 테스트 도구 | Postman | API 요청 테스트 |
| 실행 플랫폼 | 로컬 개발 서버 (localhost) | http://localhost:8080에서 API 실행 |
| 데이터베이스 | H2 (메모리) 또는 MySQL | 개발 중엔 H2, 운영 중엔 MySQL |
| 환경 설정 | .env 또는 application.yml | JWT 시크릿키 등 설정 저장 |
| JWT 라이브러리 | io.jsonwebtoken:jjwt | JWT 토큰 생성/검증 처리 |
| 암호화 라이브러리 | spring-security, BCrypt | 비밀번호 암호화 및 비교 |
우선 스택은 이렇게 사용할 예정입니다!
우선 저는 JWT 기반 로그인 API를 만들 예정이고,
회원가입 API > 로그인 API 순으로 만들어볼 예정입니다!
그러면 스타트!
원래 내가 설정해 놓은 gradle의 의존성은 기본적인 Spring Boot + JPA + Lombok + MySQL 환경을 위한 설정이었음!
JWT 로그인 API를 위해서는 JWT 관련 라이브러리와 비밀번호 암호화를 위한 spring-security가 필요하기 때문에 추가 설정
implementation 'io.jsonwebtoken:jjwt-api:0.11.5'
runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.11.5'
runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.11.5'
implementation 'org.springframework.boot:spring-boot-starter-security'
로그인 API에서는 사용자 비밀번호를 BCryptPasswordEncoder로 암호화해야 하기 때문에 필요!
나중에 JWT 인증 필터(Security Filter) 구현할 때도 꼭 필요함
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'io.jsonwebtoken:jjwt-api:0.11.5'
runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.11.5'
runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.11.5'
runtimeOnly 'com.mysql:mysql-connector-j'
implementation 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}
그리고 무조선 reload 시켜주기!
Spring boot의 설정 파일에서 데이터베이스, JPA, JWT 등 필요한 설정값들을 지정하는 단계!
현재 내가 가지고 있는 설정에서는 JWT 설정이 아직 없기 때문에, 그걸 추가해줘야함
jwt:
secret: my-very-secret-jwt-key-that-should-be-long
expiration: 3600000 # 1시간 = 1000ms * 60 * 60
server:
port: 8080
spring:
datasource:
url: jdbc:h2:mem:testdb
username: sa
password:
driver-class-name: org.h2.Driver
jpa:
hibernate:
ddl-auto: update
show-sql: true
jwt:
secret: your-super-secret-key-should-be-32-bytes-long
expiration: 3600000
본격적으로 로그인 API를 구성할 거임!
우선 기본 디렉터리 구조 + 파일 역할 정리를 해보겠음
src/
└── main/
└── java/
└── com/
└── example/
└── jwtlogin/
├── controller/
│ └── AuthController.java # 회원가입 & 로그인 API
├── dto/
│ ├── LoginRequest.java
│ ├── RegisterRequest.java
│ └── LoginResponse.java
├── entity/
│ └── User.java # 유저 엔티티 (DB 테이블과 연결)
├── repository/
│ └── UserRepository.java # DB 접근용 JPA 인터페이스
├── config/
│ └── SecurityConfig.java # 암호화, 나중에 Security 설정
└── util/
└── JwtUtil.java # JWT 생성/검증 유틸
| 디렉터리 | 파일 | 역할 |
|---|---|---|
controller/ | AuthController.java | 회원가입/로그인 요청 처리하는 API |
dto/ | LoginRequest, RegisterRequest, LoginResponse | API 요청/응답에 사용되는 데이터 클래스 |
entity/ | User.java | JPA를 통해 DB와 연결되는 사용자 테이블 엔티티 |
repository/ | UserRepository.java | 사용자 데이터를 조회/저장하는 인터페이스 |
config/ | SecurityConfig.java | 비밀번호 암호화, 추후 필터 등록 등 Security 설정 |
util/ | JwtUtil.java | JWT 생성, 검증, 만료시간 설정 등 유틸 기능 |
나의 기본 디렉토리는 com.example.qnb이기에 아래와 같이 처리해줄 것임!
com.example.qnb.login
├── controller ← API 요청 처리 (회원가입, 로그인)
├── dto ← 요청/응답 데이터 전용 클래스
├── entity ← DB와 연결될 엔티티 클래스
├── repository ← JPA Repository
├── config ← Spring Security, JWT 설정 등
└── util ← JWT 관련 유틸 클래스

최종으로 정리한 디렉토리 모습!
POST /api/auth/register 요청이 들어오면:
1. 이메일 중복 확인
2. 비밀번호 암호화
3. DB에 사용자 저장
4. 성공 메시지 응답
// 📁 com.example.qnb.login.dto.RegisterRequest.java
package com.example.qnb.login.dto;
import lombok.Getter;
import lombok.Setter;
@Getter @Setter
public class RegisterRequest {
private String userEmail;
private String userPassword;
}
@Entity
@Table(name = "USER")
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "userId")
private Integer userId;
@Column(nullable = false) //필수값
private String userPassword;
@Column(nullable = false) //필수값
private String name;
private String userNickname;
private LocalDate birthDate;
private String gender;
private String phoneNumber;
private String readingTaste;
private String profileUrl;
@Column(nullable = false, unique = true)
private String userEmail;
}
- 여기서 @Id + @GeneratedValue란?
: 새로운 User 객체를 저장할 때, userId 값을 직접 넣지 않아도,
JPA가 자동으로 DB의 AUTO_INCREMENT를 사용해서 ID를 자동 생성해주는 방식
// 📁 com.example.qnb.login.repository.UserRepository.java
package com.example.qnb.login.repository;
import com.example.qnb.login.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.Optional;
public interface UserRepository extends JpaRepository<User, Integer> {
// 이메일로 사용자 찾기 (userEmail 필드에 맞춤)
Optional<User> findByUserEmail(String userEmail);
}
// 📁 com.example.qnb.login.config.SecurityConfig.java
package com.example.qnb.login.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
@Configuration
public class SecurityConfig {
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
public String getUserEmail() {
return userEmail;
}
public void setUserEmail(String userEmail) {
this.userEmail = userEmail;
}
public String getUserPassword() {
return userPassword;
}
public void setUserPassword(String userPassword) {
this.userPassword = userPassword;
}
Caused by: java.sql.SQLSyntaxErrorException: Incorrect table definition; there can be only one auto column and it must be defined as a key
이 오류가 나서 한 한시간을 잡고 있었는데.. 알고보니 update하는 과정에서 꼬여서 내 database 자체에 table이 안들어있었다..그냥 빈 테이블로 계속 시도해본 것....힘들었다.. 다음부터는 sql 오류가 나면 db를 먼저 확인해보기!
진짜 이 부분에서 너무 많은 오류가 있었어서 정리하기도 힘들다...
401,403,500 오류 일단 모두 해결!
우선 회원가입 API는 모두 완료! 다음에는 로그인 API를 가져오겠슴다...