Spring

- DTO 객체에 inner class 적용

public class SignupDTO {

    public record Request(
            @Pattern(regexp = "^[a-z0-9]{4,10}$") String userName,
            @Pattern(regexp = "^[A-Za-z0-9]{8,15}$") String password)
    {
    }

    @Builder
    public record Response(String message) {

    }
}

DTO에 inner class를 도입해서 어떤 요청에 대한 응답 형태를 한번에 볼 수 있어 로직에 대한 이해가 쉬워질 수 있다.

여기에 정적 팩토리 메소드까지 도입하면 더 짱일듯 

 

Git

오늘 Intellij에서 작업 중 local feature branch를 따서 개발을 진행하다가 local main으로 checkout 할 일이 생겼다.  근데 건드리지도 않은 main branch에 내가 feature branch에서 작업한 untracked file들이 생겨있는 것 아닌가!!!!!

너무 당황한 채로 검색해보다가 결국 튜터님을 찾아갔는데 10초만에 대답해주셨다.

 

로컬에서 새로운 파일들은 브랜치를 옮기면 자동으로 따라간다. 파일들을 작업중인 브랜치에만 두고 싶으면 커밋에 올려두면 커밋에 올라간다.


그리고 원래 브랜치를 옮기려면 커밋을 하라고 경고를 하고. smart checkout, force checkout 이렇게 물어보는데 나같은 경우는 그냥 checkout이 되었다.

이렇게 가능한 이유는 로컬 기준으로 내 현재 브랜치가 옮기려는 브랜치보다 앞서있기 때문에 묻지 않고 가능한 것이라고 한다.

알고리즘

- 프로그래머스 : 호텔 대실(Level 2, 정렬)

느낀 점

'TIL' 카테고리의 다른 글

[23.12.11]  (0) 2023.12.12
[23.12.08]  (0) 2023.12.08
[23.12.05]  (1) 2023.12.06
[23.12.01]  (0) 2023.12.04
[23.11.30]  (1) 2023.12.01

Spring

동일성과 동등성 알아보기

 

[JAVA] 동일성(identity)와 동등성(equality) (feat. ==, equals, hashcode())

동일성(identity) 객체의 동일성이란 두 객체가 주소값까지 동일해 객체의 정보들까지 동일할 수 밖에 없는 완전히 같은 객체임을 의미한다. 동등성(equality) 객체의 동등성은 두 객체가 가지고 있

smirkdev.tistory.com

알고리즘

- 프로그래머스 : 최솟값 만들기(Level 2, 정렬)

느낀 점

오늘은 하루종일 프로젝트 설계만 했는데도 궁금증을 하나하나 가지다 보니 잘하는 팀원분이 이런 게 있다... 라고 말씀해주셔서 감사했다.

더 나아가 공부할 게 참 많다는 생각이 들었다...

'TIL' 카테고리의 다른 글

[23.12.08]  (0) 2023.12.08
[23.12.06]  (0) 2023.12.06
[23.12.01]  (0) 2023.12.04
[23.11.30]  (1) 2023.12.01
[23.11.29]  (0) 2023.11.29

동일성(identity)

객체의 동일성이란 두 객체가 주소값까지 동일해 객체의 정보들까지 동일할 수 밖에 없는 완전히 같은 객체임을 의미한다.

동등성(equality)

객체의 동등성은 두 객체가 가지고 있는 정보가 같을 때를 의미한다.

즉 동일성은 객체의 주소값과 정보 모두 같으니.

동일하다는 것은 동등한 것이지만. 동등하다는 것은 동일하다는 것은 아니다.

 == 

"==" 는 피연산자 둘의 주소값을 비교하여 주소값이 동일할 시 true, 아니라면 false를 반환한다.

즉 동일성을 즉각적으로 비교해주는 연산자이다.

 

오 reference type 끼리의 "==" 연산은 Heap에서의 주소값으로 비교를 하는군!

그러나 우리는 종종 Objects에 포함되지 않는 primitive type(int, byte, short, long, float, double, boolean, char 자료형)들에도 "==" 연산을 사용하는데 이럴 땐 어떻게 작동할까?

이들은 반복적인 변수 선언에서의 메모리 중복을 피하기 위해 Constant pool에 담겨지는데, 이 constant pool에서 가져오는 것이니 주소값을 비교한다고 보아도 무방하다.

 

eqauls

equals 메소드가 동등성을 판별해준다. 라고 말하면 엄밀히는 틀린 말이다.

왜냐하면 우리 모든 객체들의 조상 Object class에 정의된 equals를 한번 살펴보자

//Object.java
public boolean equals(Object obj) {
    return (this == obj);
}

 

놀랍게도 "==" 연산을 반환해준다.

최상위 부모 클래스에선 동일성 비교를 한다는 사실은 변함없지만 자식 클래스들은 다양한 방식으로 equals를 재정의하여 사용하고 있다.

 

가장 먼저 Object 유틸 클래스인 Objects에서는

//Objects.java
public static boolean equals(Object a, Object b) {
    return (a == b) || (a != null && a.equals(b));
}

기본적으로 "==" 연산으로 동일성 비교를 먼저 한 후 인자 a의 reference type만의 equals(만약 재정의되지 않았다면 Object.java의 동일성 비교를 함)를 통해 동등성 비교를 한다.

 

그럼 이번엔 String 클래스에 정의된 equals를 한번 보자.

//String.java
public boolean equals(Object anObject) {
    if (this == anObject) {
        return true;
    }
    return (anObject instanceof String aString)
            && (!COMPACT_STRINGS || this.coder == aString.coder)
            && StringLatin1.equals(value, aString.value);
}

 

대부분의 동등성 비교 메소드가 비슷한데. 일단 "==" 연산으로 동일성 비교를 먼저 진행해준다.

두 객체가 동일하다면 동등성은 보나마나 보장되는 것이기 때문이다.

그 다음 instanceof 로 두 객체가 같은 reference type인지 비교 후 가지고 있는 데이터가 동일한 지 각자의 개성에 따라 판단해주는 모양새다.

참고로 String은 String을 위한 contstant pool도 제공되고 있어 

String a = "haha";
String b = "haha";
//sout(a.eqauls(b)) 하면 true

이렇게 된다고 한다.

 

//List.java
boolean equals(Object o);

/**
 * Returns the hash code value for this list.  The hash code of a list
 * is defined to be the result of the following calculation:
 * <pre>{@code
 *     int hashCode = 1;
 *     for (E e : list)
 *         hashCode = 31*hashCode + (e==null ? 0 : e.hashCode());
 * }</pre>
 * This ensures that {@code list1.equals(list2)} implies that
 * {@code list1.hashCode()==list2.hashCode()} for any two lists,
 * {@code list1} and {@code list2}, as required by the general
 * contract of {@link Object#hashCode}.
 *
 * @return the hash code value for this list
 * @see Object#equals(Object)
 * @see #equals(Object)
 */

 

다음은 List interface의 equals 메소드이다. 이 친구는 List 내의 모든 원소들의 hashcode들을 이용하여 동등성을 판단해준다고 한다.

여기서 hashcode() 라는 새로운 메소드가 나오는데. 이 친구는 동일성을 판별하는데 도움을 주는 메소드이다.

 

hashcode()

일단 간략하게 설명하면 hashcode는 객체의 주소값으로 만든 하나의 고유번호이다.

//Object.java
@IntrinsicCandidate
public native int hashCode();

여기서 native 키워드는 'Java가 아닌 언어로 구현된 부분'을 JNI(java native interface)를 통해 java에서 쓰인다는 의미라고 한다.

JVM이 내부동작을 통해 객체 생성시 int 값을 반환해주는 것이다. 따라서 따로 재정의하지 않아도 모든 객체에서 사용이 가능하다.

hashcode는 주소값을 통해 얻은 고유값이라는 의미이기 때문에 hashcode가 같다면 동일한 객체라도 봐도 된다는 것이다.

그러나 여기 예외가 있는데.....

 

String.java에 재정의된 hashcode 메소드를 살펴보자.

public int hashCode() {
    int h = hash;
    if (h == 0 && !hashIsZero) {
        h = isLatin1() ? StringLatin1.hashCode(value)
                       : StringUTF16.hashCode(value);
        if (h == 0) {
            hashIsZero = true;
        } else {
            hash = h;
        }
    }
    return h;
}

이를 이해하기엔 내 식견이 너무 짧다... 주석을 보아하니

s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]

이런식으로 char 하나하나 ascii code로 받아와서 해시코드를 구해준다고 한다.

이 말은 주소값이 다른 String이라도 담긴 데이터가 같다면 hashcode가 동일하다는 것이다!

 

그런데 이뿐만이 아니다. String에 담길 수 있는 값은 무궁무진하다.

그러나 hashcode는 int형이기 때문에(0부터 0xFFFFFFFF(4,294,967,295)) 데이터가 다르더라도 hashcode는 동일한 경우도 나온다.

이로 인해 hashcode collision이란 현상도 생기게 되는데  글이 너무 길어졌으니 다음에 알아보도록 하자..

 

틀린 부분이 있다면 지적 부탁드립니다!

출처

Java hashcode()  https://brunch.co.kr/@mystoryg/133

equals와 hashcode https://mangkyu.tistory.com/101

String의 hashcode가 유일하지 않은 이유 https://blog.ggaman.com/916

'JAVA' 카테고리의 다른 글

JAVA 제어자(modifier)  (0) 2023.10.22

Spring

- Test 과정 이모저모

Optional의 null + orElseThrow

서비스단 테스트 과정에서 Repository는 Mock으로 선언하고

ToDoCard toDoCard = toDoCardRepository.findById(id).orElseThrow(() -> new NullPointerException("해당 할일카드를 찾을 수 없습니다."));

이 코드에서 찾는 할일카드가 없는 오류를 냈어야 했다. 

 

처음에는 null 받아올 테니까 

when(toDoCardRepository.findById(id)).thenReturn(null);
Throwable exception = assertThrows(NullPointerException.class, () -> {
    toDoCardService.getToDoCard(id);
});

when(toDoCardRepository.findById(id)).thenReturn(null);

이렇게 짰는데 이러면 아예 Null값 자체를 확정지어버리게 되어 오류가 발생했다.

바로 null값이 orElseThrow()를 호출할 수 없기 때문이었다.

 

when(toDoCardRepository.findById(id)).thenReturn(Optional.empty());
Throwable exception = assertThrows(NullPointerException.class, () -> {
    toDoCardService.getToDoCard(id);
});

null을 Optioanl.empty()로 고치니까 해결되었다.

앞으로 Optional에 딸린 null값은 Optional.empty로 하면 좋을 듯!

 

 

HttpStatusCode.valueof

클라이언트에게 Response를 보낼 때 ResponseEntity에 ResponseDto, header, 상태코드를 넣어주는 방식으로 반환하는 구조로 코드를 짰다.

테스트 할 때에 Dto(payload) 부분에는 상태코드가 없기 때문에 ResponseEntity에서 직접 상태코드를 받아야 한다.

그런데 ResponseEntity.getStatusCode()는 반환형이 HttpStatusCode라는 인터페이스다.

그래서 테스트 시

assertEquals(response.getStatusCode(), 200);

라고 안일하게 적었다가는 오류가 나고 만다.  

(Expected: "200 OK", Actual: "200" 이렇게 난다. 타입 자체가 다르기도 하고..)

 

assertEquals(response.getStatusCode(), HttpStatusCode.valueOf(200));
assertEquals(response.getStatusCode().value(), 200);

둘 중에 맛난 것으로 고쳐쓰자.

 

 

 

Spring Security + Controller Test

스프링 시큐리티가 적용된 채로 컨트롤러 테스트를 하려면 난감한 점이 많다.

컨트롤러 단에서는 필터 단에서의 토큰 검증이 필요가 없는데(이것 땜시 테스트 오류 발생) 필터 단에서 보내주는 UserDetails에 대한 검증은 필요하기 때문이다.(@Authentication 이 인자 부분에 달려있음!)

 

그래서 우리는 1차로

@WebMvcTest(
        controllers = {UserController.class, ToDoCardController.class, CommentController.class},
        excludeFilters = {
                @ComponentScan.Filter(
                        type = FilterType.ASSIGNABLE_TYPE,
                        classes = WebSecurityConfig.class
                )
        }
)

excludeFilter를 통해 기존 jwt 검증 필터들을 무시해주고.(websecurityconfig에서 ignore 쓰는 방법도 있다.)

 

private MockMvc mvc;

@BeforeEach
    public void setup() {
        mvc = MockMvcBuilders.webAppContextSetup(context)
                .apply(springSecurity(new MockSpringSecurityFilter()))
                .build();
    }

 

2차로 가짜 mvc 객체에 별 기능없는 커스텀필터를 적용시켜준다.

근데 진짜 별 기능없으면 안되고 UserDetails를 받아올 수 있는 최소한의 조건은 있어야 한다.

SecurityContextHolder.getContext().setAuthentication(
        (Authentication) ((HttpServletRequest) req).getUserPrincipal()
);

 

 

마지막으로

private void mockUserSetup() {
    String username = "dongha";
    String password = "12345678";
    User testUser = new User(username, password);
    UserDetailsImpl testUserDetails = new UserDetailsImpl(testUser);
    mockPrincipal = new UsernamePasswordAuthenticationToken(testUserDetails, "", testUserDetails.getAuthorities());
}

테스트를 위한 Principal까지 가져와주면 클라이언트에서 보내듯이 테스트를 진행할 수 있다..

 

 

@Test
@DisplayName("ToDoCardController, 할일카드 작성 기능")
void test2() throws Exception {
    //given
    this.mockUserSetup();
    String title = "테스트용 할일카드 제목";
    String contents = "테스트용 할일카드 내용";

    ToDoCardRequestDto requestDto = new ToDoCardRequestDto(
            title,
            contents
    );

    String toDoCardInfo = objectMapper.writeValueAsString(requestDto);

    //when - then
    mvc.perform(post("/api/todocards")
                    .content(toDoCardInfo)
                    .contentType(MediaType.APPLICATION_JSON)
                    .accept(MediaType.APPLICATION_JSON)
                    .principal(mockPrincipal)
            )
            .andExpect(status().isOk())
            .andDo(print());
}

 

알고리즘

- 프로그래머스 : 조이스틱(Level 2, 구현)

느낀 점

.

'TIL' 카테고리의 다른 글

[23.12.06]  (0) 2023.12.06
[23.12.05]  (1) 2023.12.06
[23.11.30]  (1) 2023.12.01
[23.11.29]  (0) 2023.11.29
[23.11.28]  (0) 2023.11.28

Spring

- Mockito 기초

Mockito는 Mock이라는 가짜 객체를 이용해 테스트를 용이하게 해주는 프레임워크이다.

Mock 객체는 실제 사용하는 모듈을 사용하지 않고 실제의 모듈을 "흉내"내는 "가짜" 모듈을 작성하여 테스트의 효용성을 높이는 데 사용하는 객체이며 Mockito의 핵심이다.

 

Mock 생성에는 크게 4가지 annotation이 사용된다. 바로 @Mock, @MockBean, @Spy, @InjectMocks

 

먼저 @Mock과 @MockBean은 가짜 객체 생성을 하는 어노테이션으로 쓰임새는 비슷하지만 약간의 차이점이 있다.

Spring Boot Container가 테스트 시에 필요하고, Bean이 Container에 존재한다면 @MockBean을 사용, 아니라면 @Mock 을 사용하라고 한다.

예를 들면 Service 테스트에 Repository의 주입은 필수적인데 우리는 이 repository를 사용하지는 않는다.(DB에 잘 저장되었는 지는 Repository 테스트를 따로 하기 때문에)

따라서 주입할 repository는 명목상 필요하고 repo의 메소드는 필요없기 때문에 @Mock을 선언해준다.

그러나 Controller 테스트에서 Service의 주입은 필수적임과 동시에 Service 내의 메소드를 호출해야 그 반환값이 Response로 쓰이는 경우가 많다. 이 때는 Service를 Bean으로 등록해야하기 때문에 @MockBean 을 사용한다고 나는 이해했다.

공통으로 이 가짜 Mock 객체가 어떤 기능을 하게 하고 싶다면 stubbing이라고 mock 객체 내의 메소드를 지정해주면 된다.

 

@Spy는 실제 사용되는 객체이다. (가짜 mock 사이의 진짜 spy라는 뜻인가?)

이 어노테이션을 쓰면 stubbing을 따로 하지 않으면 기존 객체의 실제 로직을 수행한다.

 

@InjectMocks 생성된 Mock 객체들을 자동으로 주입해주는 어노테이션이다.

public class CommentSerivce() {
	UserService userService;
    ToDoCardService toDoCardService;
}

이렇게 생긴 CommentService가 있다고 했을 때

@Mock
UserService userService;

@Mock
ToDoCardService toDoCardService;

@InjectMocks
CommentService commentService;

 

이렇게 해주면 @Mock으로 생성된 userService와 toDoCardService가 자동 주입된다.

알고리즘

- 프로그래머스 : 과제 진행하기(Level 2, 스택)

느낀 점

.

'TIL' 카테고리의 다른 글

[23.12.05]  (1) 2023.12.06
[23.12.01]  (0) 2023.12.04
[23.11.29]  (0) 2023.11.29
[23.11.28]  (0) 2023.11.28
[23.11.22]  (1) 2023.11.22

Spring

 - AOP

AOP(Aspect Oriented Programming)은 부가기능과 핵심기능의 관점(관심)이 다르다는 점에 집중해서 핵심기능과 부가기능을 분리하여 설계, 개발하는 것이다. 부가기능을 모듈화하여 핵심기능에 붙여준다고 생각하면 된다.

 

UserService                    PostService

before()                           before()

getUser()                        getPost()

after()                              after()

이런 식으로 각각 서비스의 조회 메소드가 실행된다 할 때 핵심기능인 get~~()는 제쳐두고

핵심기능 전후에 호출되는 before after 메소드는 공통으로 관리하자는 것이다.

가로 영역을 잘라내어 생각한다고 해서 AOP를 크로스 커팅 이라고 부르기도 한다.

 

AOP는 공통으로 쓰이는 반복되는 부가기능들을 하나로 관리하여 재사용하자는 것이며

OOP는 비즈니스 로직의 모듈화, AOP는 부가기능(로그, 오류 처리, 캐싱 등등)의 모듈화이다.

 

스프링에서는 런타임 시점에 AOP를 적용한다. 이 방식은 클래스 로더에 올라간 빈에만 적용할 수 있다는 단점이 있지만 복잡한 옵션이나 특별한 컴파일러 없이도 가능하다는 장점이 있다고 한다.

 

각종 용어들

Advice: 부가기능 로직을 정의하는 곳.(부가기능 구현 + Pointcut의 전, 후, 전후 모두 적용할 지..) 

Pointcut: Advice가 적용될 위치(어떤 컨트롤러의 어떤 메소드에 적용할 건지..)

Aspect: Advice와 pointcut들을 모듈화 한 단위. @Aspect가 Bean 클래스에만 적용이 가능하므로 @Component도 해줘야함.

 

AOP 동작방식

AOP 적용 전
AOP 적용 후

 

Spring이 프록시 객체를 중간에 삽입해 준다. 호출되는 메소드의 input, output은 변동이 없다.

알고리즘

- 프로그래머스 : 방문 길이(Level 2, 구현)

느낀 점

.

'TIL' 카테고리의 다른 글

[23.12.01]  (0) 2023.12.04
[23.11.30]  (1) 2023.12.01
[23.11.28]  (0) 2023.11.28
[23.11.22]  (1) 2023.11.22
[23.11.21]  (0) 2023.11.21

Spring

 - Spring security Role 관련 이모저모

저번 주 내내 고생한 프로젝트에서 인증 파트를 맡았는데 스프링도 잘 모르는 상태로 security 를 다루려고 하니 어려움이 많았는데 가장 시간을 많이 할애했던 Role에 대해 이야기 해보려 한다.

 

Role의 쓰임새는 사용자의 Role에 따라 특정 url에 대한 접근을 제어하기 위함이다.

가장 많이 쓰이는 메소드는 requestMatchers.~~~이다.

//Role에 관계없이 모든 사용자가 이용가능
.requestMatchers("/api/user/**").permitAll()

//Role이 USER인 사용자만 이용가능. 근데 여기서는 "ROLE"을 앞에 알아서 붙여주기 때문에 "ROLE_USER"하면 오류
.requestMatchers({uri}).hasRole("USER")

//hasRole과 동일한 기능
.requestMatchers({uri)).hasAuthority("ROLE_USER")

 

hasAnyRole 로 해서 여러 권한도 넣어줄 수 있고 한데 주의할 건 Role이 들어간 메소드는 ROLE_을 앞에 붙여준다는 의미이다. 오류 메시지에도 잘 나오니까 크게 걱정할 필요는 없다.

 

그럼 과연 이 ROLE들을 어떻게 구분하는 것일까. 비밀은 바로 UserDetails에 있다.

 

UserDetails는 security에서 제공하는 인터페이스로 사용자에 대한 인증에 이용된다.

public interface UserDetails extends Serializable {

    /**
     * Returns the authorities granted to the user. Cannot return <code>null</code>.
     * @return the authorities, sorted by natural key (never <code>null</code>)
     */
    Collection<? extends GrantedAuthority> getAuthorities();

    /**
     * Returns the password used to authenticate the user.
     * @return the password
     */
    String getPassword();

    /**
     * Returns the username used to authenticate the user. Cannot return
     * <code>null</code>.
     * @return the username (never <code>null</code>)
     */
    String getUsername();

    /**
     * Indicates whether the user's account has expired. An expired account cannot be
     * authenticated.
     * @return <code>true</code> if the user's account is valid (ie non-expired),
     * <code>false</code> if no longer valid (ie expired)
     */
    boolean isAccountNonExpired();

    /**
     * Indicates whether the user is locked or unlocked. A locked user cannot be
     * authenticated.
     * @return <code>true</code> if the user is not locked, <code>false</code> otherwise
     */
    boolean isAccountNonLocked();

    /**
     * Indicates whether the user's credentials (password) has expired. Expired
     * credentials prevent authentication.
     * @return <code>true</code> if the user's credentials are valid (ie non-expired),
     * <code>false</code> if no longer valid (ie expired)
     */
    boolean isCredentialsNonExpired();

    /**
     * Indicates whether the user is enabled or disabled. A disabled user cannot be
     * authenticated.
     * @return <code>true</code> if the user is enabled, <code>false</code> otherwise
     */
    boolean isEnabled();

}

 

함수 명들만 보더라도 사용자 인증에 가져다 쓰라고 만든 것 같다.

UserDetails에서 Role과 관련된 메소드는 getAuthorities()이다.  본인이 커스텀하기 나름이겠지만 이 함수 리턴값에 특정 유저에 대한 모든 권한 리스트를 담아주면  된다.

 

이번에 가장 헷갈렸던 부분!

hasRole("USER")가 호출 -> securitycontextholder에서 UserDetails 가져옴 -> UserDetails.getAuthorities() 에 "ROLE_USER" 가 존재하는지 확인 -> 요청을 막거나 허가.

 

컨트롤러 단에서 권한을 검증하는 @Secured @PreAuthorize도 있다.

간혹 jwt claim에 권한을 넣어주는 경우도 있는데 이는 클라이언트에게 유저의 권한을 활용할 수 있도록 하거나

jwt를 완전히 신용한다면 따로 UserDetails를 검사하지 않고 넘겨주는 필터를 적용할 수도 있겠다.

알고리즘

- 프로그래머스 : 압축(Level 2, 해시, 문자열)

느낀 점

.

 

'TIL' 카테고리의 다른 글

[23.11.30]  (1) 2023.12.01
[23.11.29]  (0) 2023.11.29
[23.11.22]  (1) 2023.11.22
[23.11.21]  (0) 2023.11.21
[23.11.20]  (0) 2023.11.20

 Spring

 - Getter, Setter의 적절한 사용

Controller를 드나드는 다양한 DTO들.. 어떤 DTO는 getter만 있기도 하고, setter만 있기도 하고, 둘 다 있기도 하고 생성자가 있기도 하다. 오늘 코드 리뷰 중 팀원 분께서 request dto는 setter가 필수라는 말씀을 해주셔서 이 이유에 대해 알아보려 한다.

 

request dto를 받을 때는 클라이언트에서 json 데이터가 넘어온다 가정을 하고 @RestController를 많이 사용한다.

이때 SAbstractJackson2HttpMessageConverter 에서 Json -> dto 객체로 변환이 일어나는데. 여기서 json의 key-value 값을 객체의 field-value 값으로 전환시켜주기 위해서는. 필드값을 세팅하기 위해 setter가 필요하다.

 

response dto를 내보낼 때에는 dto 객체를 json body로 바꾸어줘야 한다.

이제는 반대로 dto의 field-value 값을 key-value 값에 넣어주어야 하니, field-value 값을 빼낼 getter가 필요한 것이다.

 

dto <-> json body 의 변환을 위해 getter, setter 가 필요한 이유는 납득이 된다.

그렇다면 모든 dto, entity 등등에 getter, setter, allarg, noarg 다 하면 손해볼 게 없는 것 아닌가?

둘 다 남발하면 안된다!!

 

getter를 남발하면 안되는 이유.

필드를 private로 잘 설정해 놓고 모든 필드값을 getter로 꺼내올 수 있다면 캡슐화의 의미가 퇴색된다.

 

setter를 남발하면 안되는 이유.

1. setter로 인해 모든 곳에서 객체 값의 변경이 가능할 텐데, 그러면 객체 값을 변경해주는 메소드의 의미가 퇴색된다. + 의도치 않게 값이 변경될 가능성이 있기 때문에 지양해야 한다.

2. setter를 꼭 필요한 필드에만 만들어 놓으면 값을 변경하는 케이스가 희귀하다는 의미이므로 의도 파악이 명확해진다.

알고리즘

- 프로그래머스 : 방금그곡(Level 2, 문자열)

느낀 점

.

'TIL' 카테고리의 다른 글

[23.11.29]  (0) 2023.11.29
[23.11.28]  (0) 2023.11.28
[23.11.21]  (0) 2023.11.21
[23.11.20]  (0) 2023.11.20
[23.11.17]  (0) 2023.11.17

+ Recent posts