네발오징어
개발이모저모
네발오징어
전체 방문자
오늘
어제
  • 분류 전체보기
    • nodejs
    • spring
    • 알고리즘
    • DevOps
    • AWS

블로그 메뉴

  • 홈
  • 태그
  • 방명록

공지사항

인기 글

태그

  • setter
  • pre-signed-url
  • TypeScript
  • getter
  • AWS
  • S3

최근 댓글

최근 글

티스토리

hELLO · Designed By 정상우.
네발오징어

개발이모저모

Getter & Setter
nodejs

Getter & Setter

2023. 5. 30. 18:46

전에 작성 했던 코드에서 리팩터링을 원했지만 거절 당해 ChatGPT에게 이런 부분에서 부족했다는 것을 알고 적게 되었다.

 

기존 코드

Post라는 테이블의 리스트를 가져오는 OutPut Dto이다.

일단 기능적으로 만들기 급급했었다는 흔적들이 몇몇 보인다.

그 중 저 public이 맘에 들지 않았다.

 

질문


Q. getter와 setter에 대해서 설명해줘
A. Getter와 Setter는 객체지향 프로그래밍에서 클래스의 멤버 변수(속성)에 접근하는 메서드입니다. 이러한 메서드는 데이터 캡슐화를 위해 사용되며, 멤버 변수의 값을 읽거나 설정하는 데 사용됩니다.

Getter(접근자) 메서드는 멤버 변수의 값을 반환하는 역할을 합니다. 주로 변수 이름 앞에 "get" 접두사를 붙여서 작성하며, 해당 변수의 값을 읽고 외부로 반환합니다. Getter 메서드는 주로 읽기 전용 속성을 가진 변수에 사용됩니다.

예를 들어, 아래는 Java에서의 Getter 메서드의 예입니다:

private String name;

public String getName() {
    return this.name;
}

Setter(설정자) 메서드는 멤버 변수의 값을 설정하는 역할을 합니다. 주로 변수 이름 앞에 "set" 접두사를 붙여서 작성하며, 해당 변수의 값을 받아와 내부에 저장합니다. Setter 메서드는 주로 쓰기 전용 속성을 가진 변수에 사용됩니다.

아래는 Java에서의 Setter 메서드의 예입니다:

private String name;

public void setName(String name) {
    this.name = name;
}

Getter와 Setter는 멤버 변수의 접근을 제어하고, 외부에서 안전하게 변수에 접근할 수 있도록 도와줍니다. 이를 통해 데이터의 일관성과 보안을 유지할 수 있으며, 클래스의 내부 구현을 외부로부터 숨길 수 있습니다. 또한, 멤버 변수에 추가적인 로직이 필요한 경우 Getter와 Setter 내부에서 로직을 추가할 수도 있습니다.

Getter와 Setter는 자바를 비롯한 여러 프로그래밍 언어에서 일반적으로 사용되는 개념이며, 객체지향 프로그래밍의 캡슐화와 정보 은닉의 원칙을 따르기 위해 활용됩니다.

 

정리


제일 중요한 말이 있다. Getter와 Setter는 멤버 변수의 접근을 제어하고, 외부에서 안전하게 변수에 접근할 수 있도록 도와줍니다.

너무 편리한데..?

 

하지만 Getter와 Setter는 최대한 지양 하라고 배워왔는데 그 이유가 궁금했다. 

 

질문 

Q. 최대한 Getter와 setter를 쓰지 말라고 하던데 이유가 뭐야?

A. Getter와 Setter를 최대한 쓰지 말라는 것은 객체지향 설계 원칙 중에서 데이터 은닉화와 캡슐화를 강조하는 관점에서 나온 것입니다. 다음은 Getter와 Setter를 최소화하는 이유를 설명합니다.

  1. 데이터 은닉화: Getter와 Setter를 적극적으로 사용하면 객체의 내부 상태에 대한 접근이 자유롭게 노출됩니다. 이는 객체의 상태를 외부에서 마음대로 변경할 수 있으므로 데이터의 무결성을 유지하기 어려울 수 있습니다. 따라서 필요한 경우에만 Getter와 Setter를 사용하여 외부에서 접근 가능한 속성을 제한함으로써 데이터의 은닉화를 유지할 수 있습니다.
  2. 캡슐화: 객체지향 설계에서는 데이터와 해당 데이터를 조작하는 메서드를 하나의 단위로 캡슐화하여 관리하는 것이 중요합니다. Getter와 Setter를 과도하게 사용하면 객체의 메서드보다는 데이터에 초점이 맞춰지게 됩니다. 이는 객체의 행위와 상태가 분리되어 유지보수와 코드 가독성을 저하시킬 수 있습니다.
  3. 유연성과 확장성: Getter와 Setter는 객체 내부 구조가 변경되면 외부에 영향을 주는 코드 수정을 요구할 수 있습니다. 이는 유연성과 확장성을 제한할 수 있습니다. 객체 내부 구현이 변경되더라도 외부 인터페이스를 유지하고, 객체 내부 변경에 독립적인 코드를 작성하는 것이 바람직합니다.

그러나 상황에 따라 Getter와 Setter가 필요한 경우도 있습니다. 예를 들어, 객체의 속성을 읽고 수정해야 하는 경우가 있을 수 있습니다. 이런 경우에는 데이터 은닉화와 캡슐화를 고려하여 최소한의 Getter와 Setter만 사용하는 것이 좋습니다. 또한, 객체의 불변성을 유지하기 위해 Setter 대신 생성자를 활용하거나, 변경 불가능한 (immutable) 객체를 사용하는 것도 고려할 수 있습니다.

최대한 Getter와 Setter를 쓰지 않는 것은 객체지향 설계 원칙을 따르는 것이며, 응집도와 결합도가 높은 객체지향 코드를 작성하는 데 도움이 됩니다. 그러나 항상 절대적인 규칙은 아니며, 상황과 요구사항에 맞게 적절하게 사용해야 합니다.

 

정리

문제점은 객체 지향 프로그래밍의 본질에 있었다.

캡슐화

즉 데이터 은닉에서 문제가 생기는 것이다. Setter는 데이터의 변형을 Getter는 데이터의 노출로 인해 문제가 생긴다.

 

그래서 일단 ChatGPT의 개선된 코드를 한번 알아보자.

export class GetPostListOutputDto {
  private _id: number;
  private _title: number;
  private _category: number;
  private _isAnonymous: number;
  private _createdAt: number;
  private _userId: number;
  private _statusCategoryId: number;

  private _users: {
    firstName: string;
    lastName: string;
  };

  private _status: string;

  public get id(): number {
    return this._id;
  }

  public set id(value: number) {
    this._id = value;
  }

  public get title(): number {
    return this._title;
  }

  public set title(value: number) {
    this._title = value;
  }

  public get category(): number {
    return this._category;
  }

  public set category(value: number) {
    this._category = value;
  }

  public get isAnonymous(): number {
    return this._isAnonymous;
  }

  public set isAnonymous(value: number) {
    this._isAnonymous = value;
  }

  public get userId(): number {
    return this._userId;
  }

  public set userId(value: number) {
    this._userId = value;
  }

  public get statusCategoryId(): number {
    return this._statusCategoryId;
  }

  public set statusCategoryId(value: number) {
    this._statusCategoryId = value;
  }

  public get users(): { firstName: string; lastName: string } {
    return this._users;
  }

  public set users(value: { firstName: string; lastName: string }) {
    this._users = value;
  }

  public get status(): string {
    return this._status;
  }

  public set status(value: string) {
    this._status = value;
  }
}

이렇게 모두 getter와 setter를 적용시키는 것을 우선시 했다. 하지만 getter와 setter를 지양하라는 말을 놓칠 수 없기에 좀 더 찾아 보았다. 그러다 좋은 방법을 찾았다.

[참조 - https://songkg7.github.io/posts/getter-and-setter/ ]

 

방법은 이러하다.

지금 작성한 dto에서는 getter와 setter를 통해 값을 class 외부에서 변화를 주기 때문에 강한 결합이 생겨버린다. 이렇게되면 의존성 문제에 따라 내부를 수정하면 외부의 코드들도 문제가 생기기 마련이다.

 

이러한 방법으로 수정을 해보았다!

기존에 맘에 들지 않았던 부분은

public Users: {
    firstName: string;
    lastName: string;
  };
  
  ...
  
  get _isAnonymous() {
    return this.isAnonymous;
  }

public으로 선언 했기에 service layer에서 해당 값을 가져와 데이터를 변환 하였다. 간단하게 무슨 로직인지 설명하면
1. isAnonymous가 true값인지 getter로 가져온다.

2. true값이면 firstName과 lastName을 익명으로 변경해 준다.

 

getter와 setter를 모두 사용한 코드였다..

 

그렇기에 두개를 모두 제거해 보았다.

 

private Users: {
    firstName: string;
    lastName: string;
};

public anonymous() {
    if (this.isAnonymous) {
      return '익명'
    } 
    return this.Users.firstName + this.Users.lastName
}

private으로 Users 값을 변경해 주고 annonymous라는 메소드를 새롭게 선언해준다.

해당 메소드에서 class 내부의 isAnonymous값을 가져와 검증을 한 후 그에 따라 리턴값에 변화를 준다.

 

이러한 방법으로 getter와 setter를 제거했지만 이게 무조건 정답은 아니다. 또한 테스트 코드를 작성하는 시점에서는 getter와 setter를 사용하는 것이 편할 때도 있는 것이 사실이다.

하지만 여러 방법들을 통해 최대한 외부와의 의존성을 끊어내는 것이 보다 더 객체지향적인 코드를 작성하는 것이 아닐까라는 생각을 한다.

    네발오징어
    네발오징어

    티스토리툴바