강의

멘토링

로드맵

Inflearn brand logo image

인프런 커뮤니티 질문&답변

hello님의 프로필 이미지
hello

작성한 질문수

오브젝트 - 설계 원칙편

인스턴스 증가에 대한 우려

해결된 질문

작성

·

33

·

수정됨

1

안녕하세요!
'오브젝트' 책으로 공부했었는데, 강의가 있다는 걸 최근에 알게 되어 강의까지 잘 보고 있습니다!
몇 가지 궁금한 게 있어서 문의드립니다!

5-1에서 소개해주신  Schedule과 ScheduleJson를 보았는데요

제가 이전에 ScheduleJson과 비슷한 형식으로, schedule과 objectMapper를 인스턴스 변수로 가지고 json 결과를 응답하는 클래스를 만들었습니다

하지만, 코드 리뷰에서 시니어 분이 objectMapper만 가지고, schedule은 파라미터로 받는 싱글톤(bean 또는 kotlin의 object)으로 만들면 되지 않느냐고 하시더라구요
굳이 매번 인스턴스를 생성할 필요가 있냐고 하면서, 바꾸라는 식의 어조로 남기셨습니다


결국에는 싱글톤 형태의 bean으로 바꾸게 되었습니다

함수형으로 만드시길 바라시는 것 같기도 한데, 매번 인스턴스를 생성해야 한다는 것이 메모리 관련하여 크게 부담이 되는 행동인 것일까요?
싱글톤(bean)으로 만드는 것이 더 나은 방향일까요?

코드 리뷰 때로 다시 돌아간다면, ScheduleJson처럼 작성하는 방향으로 어떻게 설득시킬 수 있을까요?



그리고 다른 얘기일 수도 있긴 한데, 단순한 내용을 처리하는 클래스를 만들 때 인스턴스 변수로 넣어야 할지, 파라미터로 넣어야 할지 고민이 되더라구요

예를 들어, '요일별 메뉴가 제공된다' 라는 비즈니스 로직을 작성한다면,
다음은 요일에 해당하는 dayOfWeek를 파라미터로 작성하는 방법입니다

public class WeeklyMenu {
    public String getMenu(DayOfWeek dayOfWeek) {
        return switch (dayOfWeek) {
            case MONDAY -> "짜장면";
            case TUESDAY -> "짬뽕";
            case WEDNESDAY -> "제육덮밥";
            case THURSDAY -> "볶음밥";
            case FRIDAY -> "간짜장";
            case SATURDAY -> "미니탕수육";
            case SUNDAY -> "잡채밥";
        };
    }
}

 

다음은 dayOfWeek를 인스턴스 변수로 작성하는 방법입니다

public class WeeklyMenu {
    private final DayOfWeek dayOfWeek;

    public WeeklyMenu(DayOfWeek dayOfWeek) {
        this.dayOfWeek = dayOfWeek;
    }

    public String getMenu() {
        return switch (this.dayOfWeek) {
            case MONDAY -> "짜장면";
            case TUESDAY -> "짬뽕";
            case WEDNESDAY -> "제육덮밥";
            case THURSDAY -> "볶음밥";
            case FRIDAY -> "간짜장";
            case SATURDAY -> "미니탕수육";
            case SUNDAY -> "잡채밥";
        };
    }
}

 

선호의 차이일지, 어떠한 원칙이 있을지도 궁금합니다!

답변 2

1

조영호님의 프로필 이미지
조영호
지식공유자

hello님 안녕하세요.

강의 들어주시고 좋은 질문도 남겨 주셔서 감사합니다. 🙂

두 질문에 대해 차례대로 답변 드리도록 할게요!

 

1.ObjectMapper를 싱글턴으로 만들지 여부

강의에서 사용한 예제 코드의 경우에는 단일 책임 원칙에 초점을 맞추고 예제의 구현을 단순화하기 위해 메서드 내부에서 ObjectMapper를 생성하고 있지만, 다수의 요청을 동시에 처리해야 하는 실무 코드에서는 ObjectMapper를 싱글톤으로 만든 후에 필요한 경우에 의존성 주입을 받아 사용하시는게 좋습니다.

사이즈가 작고 단순한 객체라면 메서드 내부에서 지역 객체로 생성하거나 인스턴스 변수로 선언하고 매번 생성해도 무방하지만 ObjectMapper처럼 객체 초기화 비용이 큰 경우에는 하나의 인스턴스를 공유해서 사용하는게 효과적입니다.

이렇게 여러 객체가 하나의 인스턴스를 공유해서 사용하기 위해서는 이 인스턴스가 쓰레드에 안전(thread-safe)해야 하는데, 다행히도 ObjectMapper의 경우에는 쓰레드에 안전하게 설계되어 있습니다.

따라서 여러 인스턴스가 ObjectMapper를 공유해서 쓰더라도 문제가 발생하지 않습니다.

다만 ObjectMapper의 상태를 설정하는 setter 계열 메서드는 쓰레드에 안전하지 않기 때문에 애플리케이션이 구동되는 시점에 필요한 상태를 가지도록 ObjectMapper를 초기화하고, 애플리케이션이 실행되는 동안에는 ObjectMapper의 상태를 바꾸지 말아야 합니다.

Spring을 사용할 경우에는 애플리케이션 구동 시점에 아래 코드처럼 필요한 상태를 가지는 ObjectMapper 싱글톤을 생성해서 애플리케이션 컨텍스트에 등록한 후 필요한 곳에서 의존성 주입해서 사용하는 방식으로 구현합니다(Boot의 경우에는 기본 ObjectMapper가 미리 등록되어 있어 빈 등록 과정 없이 의존성 주입 받아 사용할 수 있습니다).

@Configuration
public class ObjectMapperConfig { 
    @Bean // ObjectMapper 객체 빈 등록 , 빈의 이름은 메소드 이름을 따라간다
    public ObjectMapper objectMapper() {
        ObjectMapper mapper = new ObjectMapper();
        mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
        mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        mapper.registerModule(new JavaTimeModule());      
        // 필요한 설정 추가
	...
        return mapper;
    }
}

 

2.인스턴스 변수와 메서드 파라미터의 차이

첫 번째 차이점은 객체 인스턴스의 의미가 다르다는 점입니다.

질문에 적어주신 코드를 예로 들어서 어떤 의미적인 차이가 있는지 설명해 드릴게요.

먼저 dayOfWeek을 파라미터로 받는 WeeklyMenu는 “모든 요일의 메뉴 정보를 다 관리하는 책임”을 맡고 있는 객체를 의미합니다.

따라서 이 객체는 어떤 요일의 메뉴를 물어보더라도 다 대답을 할 수 있죠.

public class WeeklyMenu {
    public String getMenu(DayOfWeek dayOfWeek) {
        return switch (dayOfWeek) {
            case MONDAY -> "짜장면";
            case TUESDAY -> "짬뽕";
            case WEDNESDAY -> "제육덮밥";
            case THURSDAY -> "볶음밥";
            case FRIDAY -> "간짜장";
            case SATURDAY -> "미니탕수육";
            case SUNDAY -> "잡채밥";
        };
    }
}

반면에 dayOfWeek을 인스턴스 변수로 가지는 WeeklyMenu는 “특정한 하나의 요일의 메뉴 정보만 관리하는 책임”을 맡고 있는 객체를 의미합니다.

public class WeeklyMenu {
    private final DayOfWeek dayOfWeek;

    public WeeklyMenu(DayOfWeek dayOfWeek) {
        this.dayOfWeek = dayOfWeek;
    }

    public String getMenu() {
        return switch (this.dayOfWeek) {
            case MONDAY -> "짜장면";
            case TUESDAY -> "짬뽕";
            case WEDNESDAY -> "제육덮밥";
            case THURSDAY -> "볶음밥";
            case FRIDAY -> "간짜장";
            case SATURDAY -> "미니탕수육";
            case SUNDAY -> "잡채밥";
        };
    }
}

만약 이 객체를 다음과 같이 생성했다면 이 객체는 월요일의 메뉴에 대해서만 대답할 수 있습니다.

WeeklyMenu menu = new WeeklyMenu(DayOfWeek.MONDAY);

다시 말해서 파라미터로 받는 WeeklyMenu는 “모든 요일의 메뉴”를 의미하고, 인스턴스 변수로 가지는 WeeklyMenu는 “특정한 요일의 메뉴”를 의미합니다.

이 의미적인 차이는 코드의 의도를 전달할 때 매우 큰 차이를 보입니다.

두 번째 차이점은 방금 전에 설명드린 의미적인 차이점으로부터 파생되는 내용인데, 파라미터로 받는 WeeklyMenu는 어떤 요일의 메뉴 정보가 필요한지를 클라이언트가 판단하고 결정합니다.

즉, 요일을 관리할 책임이 클라이언트에게 있고, WeeklyMenu는 클라이언트가 원하는 요일의 메뉴를 전달해주기만 하면 됩니다.

반면에 인스턴스로 가지는 WeeklyMenu의 경우에는 요일을 관리할 책임이 WeeklyMenu에게 있습니다.

클라이언트는 어떤 요일인지 모르는 상태에서 WeeklyMenu가 반환해주는 메뉴를 받기만 합니다.

따라서 변화하는 값(이 경우에는 요일)을 클라이언트가 알고 요청해야 하는지, 아니면 그 값이 객체의 본질적인 의미여서 클라이언트는 단지 객체가 알아서 판단해주기를 바라는지에 따라인스턴스 변수를 사용할지, 파라미터를 사용할지를 결정하게 됩니다.

 

답변이 되었는지 모르겠네요. 😊

hello님의 프로필 이미지
hello
질문자

답변 정말 감사합니다!
잘 이해되었습니다

사실 질문 1의 핵심은 '인스턴스 생성을 많이 하는 것이 메모리에 부담이 되는 행동인가요?' 였습니다.
강의에 나온 예제 코드로 질문을 드리다 보니, 좀 특이 케이스인 ObjectMapper로 예시를 들게 되었네요

제가 실제로 작성했던 코드는 WeeklyMenu에 가까운 수준의 비교적 간단한 기능이었습니다. 저런 걸 굳이 매번 객체 생성하는 것보다는 kotlin의 object 같은 걸로 만들면 되지 않느냐 라는 시니어의 코드 리뷰였습니다.

객체지향 프로그래밍은 유지보수성 등의 장점이 있기 때문에, 메모리 사용량의 증가는 어느 정도 감수해야 하는 상황인걸까요? 아니면 JVM 자체가 짧은 생명 주기를 가진 인스턴스 관리를 잘 해주고 있으니, 메모리 사용에 있어서 크게 부담을 느낄 필요는 없는 걸까요?

조영호님의 프로필 이미지
조영호
지식공유자

어떤 부분이 궁금하신 건지 정확하게 이해했습니다. 🙂

'인스턴스 생성을 많이 하는 것이 메모리에 부담이 되는 행동인가요?’에 대한 답은 그럴 수도 있고 아닐 수도 있다입니다.

 

hello님의 이번 질문은 객체의 크기가 작고 생성 비용이 부담이 되지 않는 경우에 대해 질문주신 것으로 판단되는데요 이런 경우라면 큰 걱정하지 마시고 편하게 객체를 생성하셔도 무방합니다.

최근의 JVM은 작은 객체를 생성하고 제거하는데 드는 비용이 크지는 않기 때문에 코드를 읽기 쉽고 유지보수하기 쉽게 만들기 위해 ‘작은’ 객체들을 생성하는 것은 일반적으로 권장되는 일입니다.

강의에서 설명드린 값 객체가 가독성을 높이고 부수효과를 방지해서 유지보수성을 향상시키기 위해 작은 객체들을 생성하는 경우라고 볼 수 있습니다.
(앞의 답변에서 말씀드린 것처럼 인스턴스의 크기가 크거나 ObjectMapper나 데이터베이스 Connection처럼 생성할 때의 비용이 크다면 인스턴스 생성을 많이 하는 것은 메모리에 부담이 될뿐만 아니라 성능에도 악영향을 미칩니다.)

 

그렇다고 하더라도 가독성이나 유지보수성 관점에서 특별한 장점이 없다면 불필요하게 객체를 생성하는 일은 피하는게 좋습니다.

특히나 불변(immutable) 객체이거나 무상태(stateless) 객체라면 불필요하게 객체를 생성하기 보다는 객체를 재사용하는게 효과적입니다.

예로 들어주신 dayOfWeek을 파라미터로 받는 WeeklyMenu의 경우에는 요청 사이에 상태를 공유하지 않는 무 상태 객체이기 때문에 유지보수성 측면에서 장점이 없다면 매번 생성하는 것보다는 하나의 객체를 공유하는게 더 좋습니다.

dayOfWeek을 상태로 가지는 WeeklyMenu 객체의 경우에는 약간 애매한데 이 객체가 불변 객체일 경우에는 요일이 7개밖에 없기 때문에 7개의 객체를 생성한 후 이 객체들을 매번 재사용하는 것도 가능할 것입니다.

하지만 이 객체들의 참조를 어디선가 유지하려면 유지보수성 측면에서 불필요한 복잡도가 추가되기 때문에 이 경우에는 새로운 객체를 생성해서 사용하는게 더 좋은 방법입니다.

 

결론적으로 작은 객체들을 생성하는데 드는 비용을 고민하지 않는게 바람직합니다.

하지만 유지보수성 측면에서 특별한 장점이 없는 불변 객체나 무상태 객체를 다루는 경우에는 불필요하게 객체를 생성하기 보다는 동일한 객체를 재사용하는게 좋습니다.

 

첫 번째 질문에서 말씀하신 케이스는 ObjectMapper의 생성 비용이 클뿐만 아니라 Schedule을 파라미터로 받을 경우 해당 객체를 무상태 객체로 만들 수 있고, 새로운 객체를 매번 만든다고 해서 유지보수성 측면에서 큰 장점이 보이지 않기 때문에 사수님의 말씀처럼 메모리 사용량이나 성능 측면에서 새로운 인스턴스를 생성하기 보다는 하나의 객체를 재사용하는게 적합해 보입니다.

 

답변이 되었는지 모르겠네요. 😊

감사합니다.

0

안녕하세요, 인프런 AI 인턴입니다. hello님!

주제 자체가 객체 생성과 관련된 디자인 결정으로 굉장히 흥미로운 부분입니다. 여러분이 질문한 부분은 메모리 관리와 객체지향 설계에서 지속적으로 고민되는 주제인데요, 몇 가지 관점을 나누어 설명드리겠습니다.

1. 객체 생성과 메모리 관리

객체를 자주 생성하는 것이 메모리적으로 부담이 되는 상황이라기보다는, 시스템의 요구사항과 설계 전반에 걸친 의사 결정 문제라고 볼 수 있습니다. 작은 규모의 클래스라면 인스턴스를 빈번히 생성하더라도 큰 영향을 미치지 않는 경우가 많습니다. 그러나 다음과 같은 경우에는 싱글톤 패턴이 적합할 수 있습니다:

  • 성능: 객체 생성의 비용이 높은 경우.
  • 상태가 불변: 여러 곳에서 동일한 상태를 공유하기 위해 불변 객체를 서로 공유할 때.
  • 공유자원: 자원을 공통으로 사용해야 하는 경우.

2. 싱글톤과 빈의 활용

스프링이나 다른 의존성 주입 프레임워크에서는 싱글톤 스콥의 빈을 활용하여 객체를 공유하는 것이 일반적입니다. 이는 메모리 사용을 줄이고, 객체 상태의 일관성을 유지하는 데 유리할 수 있습니다. 다만, 이 역시 트레이드오프가 있으며, 상태를 공유하는 경우에는 상태 관리에 주의해야 합니다.

3. 상태를 인스턴스 변수 vs 파라미터로

주어진 두 가지 코드 예시 중에 어떤 방법을 사용할지는 대상 클래스의 책임과 설계 의도에 따라 결정됩니다.

  • 파라미터로 전달: 간단한 유틸리티 메서드처럼 여러 번 호출될 것이고 매번 다른 인자를 받아 다양한 결과를 반환하는 경우.

  • 인스턴스 변수로 저장: 특정 상태를 기반으로 여러 작업을 수행하거나, 객체의 유효기간 동안 불변 상태를 유지해야 하는 경우 (Immutable 객체 설계).

유사한 질문과 답변으로는 아래 링크들을 확인해 주세요:

  • DAO 관련 질문드립니다 : 객체지향 설계에서 공통 자원의 사용과 메모리 효율성을 논의한 Community의 다른 사례입니다.

이러한 결정은 항상 상황에 따라 달라질 수 있으며, 코드를 몇 가지 기준에 따라 설계하는 것이 좋습니다. 설명이 충분치 않다면 추가적인 의견을 주시면 더 자세히 알아보겠습니다.

저는 질문자님의 이해를 돕고자 지식공유자님의 기존 답변을 참고하여 유사한 답변 링크를 추천해드리고 있습니다. 현재 베타 기능으로 답변이 만족스럽지 않을 수 있는 점 양해 부탁드립니다. 🙏
추가적으로 궁금한 점이 있으시면, 이어서 질문해 주세요. 곧 지식공유자께서 답변해 주실 것입니다.

hello님의 프로필 이미지
hello

작성한 질문수

질문하기