[Spring] Getter, Setter (+Mapping)

Hood·2025년 1월 10일

Spring Boot

목록 보기
6/15
post-thumbnail

✍ Back-End 지식을 늘리자!

백엔드 개발자를 준비하면서 생긴 궁금증을 정리한 포스트입니다.


들어가기 전

데이터베이스와 연결되는 객체를 만들다 보면,
외부로부터 데이터를 보호하고 접근 로직을 분리하기 위해
자바의 private 접근 제어자를 사용하게 됩니다.

자바의 접근 제어자에는 다음과 같이 4가지 종류가 있습니다.

  • private : 같은 클래스 내에서만 접근 가능
  • default : 같은 패키지 내에서만 접근 가능
  • protected : 같은 패키지 내 또는 자손 클래스에서 접근 가능
  • public : 접근 제한 없음

이번 포스트에서는 데이터를 보호하기 위한 접근 방식 중 하나로,
은닉된 멤버 변수에 접근할 때 사용하는 Getter, Setter의 개념을 정리해보겠습니다.


Getter, Setter

GetterSetter는 객체의 속성(데이터)을 읽거나 수정할 때 사용하는 메서드입니다.
즉, 객체의 데이터를 보호하면서도 필요한 방식으로 접근할 수 있도록 도와주는 중요한 개념입니다.

참고로 Kotlin에서는 프로퍼티를 선언하면 기본 gettersetter가 자동으로 생성됩니다.
다만 값 검증이나 가공이 필요할 때는 직접 커스텀해서 사용할 수 있습니다.


Getter란?

Getter는 값을 읽을 수 있도록 해주는 접근자입니다.
즉, 객체의 속성 값을 외부에서 조회할 때 사용합니다.

private class Person {
    var name: String = "John"
        get() = field
}

fun main() {
    val person = Person()
    println("이름: ${person.name}")
}

// 출력: 이름: John

위 예제에서는 Person 클래스 안에 name의 기본값을 "John"으로 두고,
getter를 통해 해당 값을 읽어오도록 하였습니다.

private 접근 제어자를 사용하면 외부에서 클래스 내부 구현에 직접 접근하는 것을 제한할 수 있고,
getter는 필요한 값만 안전하게 꺼내서 사용할 수 있도록 도와줍니다.


Setter란?

Setter는 값을 변경할 수 있도록 해주는 접근자입니다.
즉, 객체의 속성 값을 수정할 때 사용합니다.

private class Person {
    var name: String = "John"
        set(value) {
            field = value
        }
}

fun main() {
    val person = Person()
    person.name = "Alice"
    println("이름: ${person.name}")
}

// 출력: 이름: Alice

위 예제에서는 name의 기본값을 "John"으로 두고,
setter를 통해 새로운 값으로 변경할 수 있도록 하였습니다.

이후 main 함수에서 "Alice"를 대입하면 setter가 호출되고,
변경된 값이 다시 출력되는 것을 확인할 수 있습니다.


Getter와 Setter를 함께 사용해보면?

private class Person {
    var age: Int = 0
        get() = field
        set(value) {
            if (value >= 0) field = value
            else println("나이는 0 이상이어야 합니다.")
        }
}

fun main() {
    val person = Person()
    person.age = 25
    println("나이: ${person.age}")
    person.age = -5
}

// 출력:
// 나이: 25
// 나이는 0 이상이어야 합니다.

이번 예제에서는 age 값이 0 이상일 때만 저장되도록 setter에서 조건을 검사하고 있습니다.

코드를 보면 예상할 수 있듯이,
25를 넣으면 정상적으로 값이 저장되고 getter를 통해 나이: 25가 출력됩니다.
반대로 -5를 넣으면 조건을 만족하지 못하므로 값이 저장되지 않고,
대신 "나이는 0 이상이어야 합니다."라는 메시지가 출력됩니다.

이처럼 setter를 사용하면 단순히 값을 바꾸는 것에서 끝나는 것이 아니라,
잘못된 데이터가 들어오는 것을 미리 막는 검증 로직도 함께 작성할 수 있습니다.


+ Mapping

매핑(Mapping)이란 서로 다른 두 대상을 연결하는 과정을 의미합니다.

일상적인 예로는 실제 지형을 지도 위에 표현하는 것,
또는 어떤 정보를 다른 형식에 맞게 대응시키는 것을 떠올릴 수 있습니다.

그렇다면 프로그래밍, 특히 JPA에서의 매핑은 무엇을 의미할까요?

바로 객체의 데이터와 데이터베이스의 구조를 연결하는 과정을 의미합니다.

예를 들면 다음과 같습니다.

  • 객체의 필드 ↔ 데이터베이스의 컬럼
  • 객체 간의 관계 ↔ 데이터베이스 테이블 간의 관계(FK)

JPA에서의 매핑

테이블과 객체를 단순 매핑한다면?

@Entity
data class User(
    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    val id: Long = 0,

    @Column(name = "user_name", nullable = false)
    var name: String
)

위 예제에서는 @Entity를 사용해 User 클래스를 데이터베이스 테이블과 매핑하였고,
@Column을 사용해 name 필드를 user_name 컬럼과 연결하였습니다.

즉, 이 예시는 엔티티와 테이블, 그리고 필드와 컬럼이 어떻게 연결되는지를 보여주는 기본적인 매핑 예제입니다.


그렇다면 1:N 관계의 매핑은?

@Entity
data class User(
    @Id @GeneratedValue
    val id: Long = 0,
    var name: String,

    @OneToMany(mappedBy = "user", cascade = [CascadeType.ALL])
    val posts: MutableList<Post> = mutableListOf()
)

@Entity
data class Post(
    @Id @GeneratedValue
    val id: Long = 0,
    var content: String,

    @ManyToOne
    @JoinColumn(name = "user_id")
    val user: User
)

위 예제에서 UserPost는 1:N 관계를 가집니다.
즉, 한 명의 User는 여러 개의 Post를 가질 수 있습니다.

그래서 User 클래스에는
@OneToMany(mappedBy = "user")를 사용해 여러 개의 Post를 참조하도록 작성하였고,

반대로 Post 클래스에는
@ManyToOne을 사용해 하나의 User를 참조하도록 작성하였습니다.

또한 @JoinColumn(name = "user_id")를 사용해
Post 테이블의 user_id 컬럼을 외래 키로 설정하였습니다.

이처럼 JPA에서는 어노테이션을 통해
객체 간 관계를 데이터베이스 관계와 연결할 수 있습니다.


📌 결론

GetterSetter는 객체의 데이터를 보호하고 제어하기 위해 사용하는 접근자입니다.

이를 사용하는 대표적인 이유는 다음과 같습니다.

  1. 외부에서 데이터를 직접 수정하지 못하도록 하여
    데이터의 일관성과 캡슐화를 지키기 위해
  2. 데이터를 읽고 수정하는 방식과 내부 저장 방식을 분리하여
    유지보수성과 확장성을 높이기 위해

또한 Mapping은 객체와 데이터베이스를 자연스럽게 연결하기 위해 사용됩니다.

즉, GetterSetter는 객체 내부 데이터를 안전하게 다루기 위한 개념이고,
Mapping은 그 객체를 데이터베이스 구조와 연결하기 위한 개념이라고 정리할 수 있습니다.

profile
달을 향해 쏴라, 빗나가도 별이 될 테니 👊

0개의 댓글