이 글은 회원정보 데이터베이스와 API를 설계하는 방법에 대해 정리한 글입니다.
JPA (Java Persistence API)
이 글에서 하고자 하는 핵심 작업은 "자바 클래스를 만들어서 데이터베이스 테이블로 변신시키는 것"이다. 이 기술을 JPA(Java Persistence API)라고 한다.
JPA의 핵심 개념
@Entity(엔티티)- 평범한 자바 클래스에
@Entity어노테이션이 추가되면, JPA는 "이 클래스는 자바 객체가 아니라 데이터베이스에 저장해야 할 테이블 설계도구나!"라고 생각한다. @Entity어노테이션이 붙으면, SQL(ex.CREATE TABLE Member ...)을 직접 짜지 않아도 스프링이 알아서 DB에 테이블을 만들어준다.
- 평범한 자바 클래스에
@Id(PK, Primary Key)- 데이터베이스 테이블에는 반드시 행(Row)을 구분하는 유일한 식별자(Primary Key, PK)가 있어야 한다. (만약 중복되는 데이터가 있다면, 해당 이름은 식별자가 될 수 없다.)
- 해당 변수가 이 테이블의 유일한 식별자(PK)라고 지정하는 것이다.
- 보통
Long id라는 변수 위에 붙인다.
@GeneratedValue- 번호를 신경쓰지 않아도 데이터베이스에서 알아서 1씩 증가시켜서 번호를 부여해준다. (Auto Increment)
- 보통
GenerationType.IDENTITY라는 옵션과 함꼐 사용한다.
Spring Boot의 기본 디렉토리 구조
(정답은 아니지만...) Spring Boot에서는 주로 계층(Layer) 별로 패키지를 나누는 구조를 가장 많이 사용한다. (역할에 따라 디렉토리 구분)
이러한 기조에 맞춰서 프로젝트의 디렉토리는 아래와 같이 구성하였다.
📁 src/main/java/com/example/backend
│
├──📁 controller # 요청을 받고 응답을 주는 곳
├──📁 service # 실제 비즈니스 로직(로그인 처리 등)을 하는 곳
├──📁 repository # DB에 접근해서 데이터를 가져오는 곳
├──📁 entity # DB 테이블과 매칭되는 클래스 모음
└──📁 dto # 데이터를 주고받을 때 쓰는 객체 모음
데이터베이스 연결 및 테이블 생성 (with. PostgreSQL)
디렉토리도 만들었으니, 데이터베이스를 생성해보고자 한다. 데이터베이스는 최근 점차 많이 사용되고 있는 PostgreSQL을 사용해보고자 한다. 만약 PostgreSQL이 설치되어 있지 않다면, 아래 링크에서 먼저 설치를 해야한다.
PostgreSQL: The world's most advanced open source database
PostgreSQL
The world's most advanced open source database.
www.postgresql.org
먼저 PostgreSQL를 사용하기 위한 의존성을 추가해야 한다. build.gradle 파일의 dependencies { ... } 부분에 runtimeOnly 'org.postgresql:postgresql'을 추가한다.
...
dependencies {
...
runtimeOnly 'org.postgresql:postgresql'
}
...
의존성 구성(Dependency Configuration) 키워드를 runtimeOnly로 설정한 이유는 자바의 표준 데이터베이스 기술인 JDBC( java.sql.* )만 사용해서 코드를 짜도록 강제하기 위함이다. 의도된 불편함으로, 표준을 지킴으로서 추후에 데이터베이스를 바꾸고자 할 때, 자바 코드는 변경하지 않고 설정 파일만 바꿔서 바로 갈아탈 수 있는 유연성을 확보할 수 있다.
implementationvsruntimeOnlyvscompileOnly
👉implementation
✅ 의미: 코딩할 때에도 쓰고, 실행할 때에도 쓴다.
✅ 상태: 코드를 짤 때 import 해서 직접 가져다 쓸 수 있고, 프로그램이 돌아갈 때에도 계속 존재한다.
👉runtimeOnly
✅ 의미: 코딩할 땐 안보이지만, 실행할 때 몰래 쓱 가져온다.
✅ 차이점: 코드에서 직접import할 수 없게 막히게 된다.
👉compileOnly
✅ 의미: 코딩할 때에만 쓰고, 실행할 땐 버리고 간다.
✅ 이점: 실제 결과물인 프로그램에 포함되지 않기 때문에 메모리(용량)에 이점이 있다.
의존성 추가가 끝났다면, application.properties 설정을 진행한다.
PostgreSQL에 접속하기 위해서는 주소(URL), 아이디, 비밀번호가 반드시 필요하다. 아래 코드를 src/main/resources/application.properties 파일에 추가한다. 이때 DB는 새롭게 만들어주는 것이 좋다. (postgres DB는 주로 관리형으로 사용하기 때문에 다른 DB를 하나 생성하고, 생성한 DB를 Spring Boot에 연결시켜 주는 것이 좋다.)
# 1. 접속 주소 (jdbc:postgresql://주소:포트/DB이름)
spring.datasource.url=jdbc:postgresql://localhost:5432/postgres
# 2. 아이디 (기본값은 보통 postgres)
spring.datasource.username=postgres
# 3. 비밀번호 (설치할 때 설정한 것)
spring.datasource.password=여기에_비밀번호_입력
# 4. 드라이버 클래스 (생략 가능하지만 명시하는 게 좋음)
spring.datasource.driver-class-name=org.postgresql.Driver
# 5. JPA가 테이블을 관리하는 방식
# 데이터를 유지할 필요가 없으며, 업데이트 내용이 매번 바뀌었으면 좋겠으면 update 대신 create
spring.jpa.hibernate.ddl-auto=update
이제, 실제 테이블을 만들기 위해서 com.example.backend.entity 안에 Member.java 파일을 생성한 후 아래와 같이 작성했다. 핵심은 @Entity 어노테이션을 넣는 것, 그리고 Getter/Setter를 위한 Lombok 어노테이션을 사용한다는 것이다.
package com.devkuk.backend.entity;
import jakarta.persistence.*;
import lombok.Getter;
import lombok.Setter;
@Entity @Getter @Setter
public class Member {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column
private String userId;
@Column
private String userPw;
@Column
private String email;
@Column
private String name;
@Column
private Integer age;
@Column
private String gender;
@Column
private String phoneNumber;
}
Java에서는 이름 짓는 규칙이 매우 엄격하게 동작하기 때문에 아래 규칙을 반드시 지켜줘야 한다.
- 클래스: 대문자로 시작(PascalCase) 👉 ex.
Member - 변수(필드): 소문자로 시작(camelCase) 👉 ex.
userId
만약 변수명을 대문자로 시작한다면, 라이브러리(Lombok 등)가 Getter/Setter를 만들 때 오류가 발생하거나 DB 칼럼명 매핑이 제대로 이루어지지 않을 수 있다.
여기까지 작성하고, 코드를 실행시키면 postgreSQL에 새로운 테이블이 생성되는 것을 확인할 수 있다.

최대 길이나 Null값 허용 여부 등을 추가로 설정해줘야 하는데, 이 역시도 Java 코드로 할 수 있다.
어노테이션에 옵션을 넣어주면 되는데, 대표적인 옵션은 아래와 같다.
nullable: Null 값 혀용 여부 (기본값:true)unique: 유일성 여부 (기본값:false)length: 최대 길이 지정 (기본값:255)
최종적으로는 데이터의 길이와 타입을 고려하여 아래와 같이 작성했고, 그 결과는 아래 이미지와 같다.
package com.devkuk.backend.entity;
import com.devkuk.backend.constant.Gender;
import jakarta.persistence.*;
import lombok.Getter;
import lombok.Setter;
@Entity @Getter @Setter
public class Member {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false, unique = true, length = 30)
private String userId;
@Column(nullable = false, length = 30)
private String userPw;
@Column(nullable = false, unique = true, length = 50)
private String email;
@Column(nullable = false)
private String name;
@Column
private Short age;
@Column(length = 10) @Enumerated(EnumType.STRING)
private Gender gender;
@Column(length = 11)
private String phoneNumber;
}

🤔 어? Data type에서 가변 길이가 변하지 않았는데요?!
postgreSQL 특징인지 Data type에서는 글자수 제한이 따로 표시되지 않는다. 다만, 좌측 Object Explorer를 살펴보면 Columns 부분에서 몇 글자로 제한되어 있는지 확인할 수 있다.
이렇게 Member 테이블 완성!!

레포지토리(Repository) 생성
데이터베이스의 테이블까지 새롭게 만들었으니, 이제 레포지토리를 생성해볼 차례이다. 레포지토리는 Java 언어로 명령을 전달하면, 자동으로 SQL로 번역해서 DB에 전달하는 역할을 한다. 즉, Java와 DB 사이에 통역사이자 관리인 역할을 하는 것이다.
그럼 본격적으로 레포지토리를 만들어보자.
Member 테이블을 관리하는 MemberRepository.java를 com.example.backend.repository 안에 생성한다.
package com.devkuk.backend.repository;
import com.devkuk.backend.entity.Member;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.Optional;
// class가 아니라 interface로 생성
// JpaRepository<Entity, Type of PK> -> Entity: Member, id Type: Long
public interface MemberRepository extends JpaRepository<Member, Long> {
// 기본 CRUD는 자동 생성
// save(), findById(), findAll(), delete() 등
// 주문형 기능 정의
// 1. userId 기반 탐색
// SQL: SELECT * FROM member WHERE user_id = ?
Optional<Member> findByUserId(String userId);
// 2. email 기반 탐색
// SQL: SELECT * FROM member WHERE email = ?
Optional<Member> findByEmail(String email);
}
extends JpaRepository<Entity, Type of PK>:extends JpaRepository<Member, Long>- Entity: 해당 레포지토리 인터페이스가 담당할 테이블 이름
- Type of PK: PK의 타입 정의
Optional<Entity>:Optional<Member>- 있을 수도 있고, 없을 수도 있는 상자
- Optional 방식은 이전에 값이 없으면
null값을 반환해서 발생하던 문제를 해결하기 위해 원하는 값이 없더라도 항상 객체로 반환 👉 값이 있으면 객체 안에 값을 담아서 반환하고, 값이 없으면null을 담아서 반환
- 쿼리 메서드 (Query Methods)
findByUserId처럼 이름을 규칙에 맞게 작성하면 스프링이 이름을 분석해서 자동으로 SQL을 생성- findBy (찾아라) + UserId (userId 칼럼에서) =
SELECT * FROM member WHERE user_id = ...
레포지토리 테스트 코드
데이터를 넣는 테스트 코드(Test Code)를 통해 잘 동작하는지 확인해보고자 한다.
src/test/java/com.example.backend 패키지 안에 테스트 코드를 생성하면 된다. MemberRepositoryTest.java 파일을 생성하고 아래와 같이 코드를 작성하였다.
package com.devkuk.backend;
import com.devkuk.backend.constant.Gender;
import com.devkuk.backend.entity.Member;
import com.devkuk.backend.repository.MemberRepository;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
//@Transactional // 테스트가 끝나면 데이터를 깔끔하게 지워주는 역할 (눈으로 데이터를 확인하기 위해서는 주석처리 필요)
class MemberRepositoryTest {
@Autowired
MemberRepository memberRepository;
@Test
void signedUpTest() {
// 회원 정보 만들기
Member member = new Member();
member.setUserId("testID");
member.setUserPw("1234");
member.setEmail("testID@test.com");
member.setName("테스트");
member.setAge((short)25);
member.setGender(Gender.MALE);
member.setPhoneNumber("01012345678");
// 저장하기
// SQL: INSERT INTO member ...
memberRepository.save(member);
System.out.println("================");
System.out.println("저장 성공!!");
System.out.println("================");
}
}

정상적으로 테이블에 데이터가 추가된 것을 확인할 수 있다!
'🖥️ Dev > Backend' 카테고리의 다른 글
| [Backend][Spring Boot] 회원정보 데이터베이스를 API로 만들어보자! (1) | 2026.01.27 |
|---|---|
| [Backend][Spring Boot] Spring Boot하다가 다시 정리하는 Java 기초: 접근 제어자, static, Annotation (0) | 2026.01.26 |
since 2025.01.27. ~ 개발자를 향해....🔥