design-patterns

5 개의 포스트

line원문

코드 품질 개선 기법 21편: 생성자를 두드려 보고 건너라 (새 탭에서 열림)

객체의 상태에 따라 특정 메서드 호출이 제한되는 설계는 런타임 에러를 유발하는 주요 원인이 됩니다. 개발자는 주석이나 문서에 의존하기보다, 언어의 문법적 특성을 활용해 '애초에 잘못 사용할 수 없는 구조'를 설계해야 합니다. 이를 위해 생성 시점에 초기화를 완료하거나, 지연 초기화 또는 상태를 분리한 타입을 활용해 객체의 안전성을 보장하는 것이 핵심입니다. **초기화 시점에 로직 실행과 팩토리 함수 활용** 객체를 생성하는 즉시 필요한 준비 작업을 마치는 방식입니다. * **생성자 및 init 블록 사용**: 모든 속성을 생성 시점에 결정하여 읽기 전용(`val`)으로 선언할 수 있어 객체의 불변성을 유지하기 좋습니다. * **생성자의 제약 사항**: 생성자 내에서는 `suspend` 함수 호출이 불가능하며, 복잡한 로직이나 부작용이 큰 코드를 작성할 경우 초기화되지 않은 속성에 접근하는 버그가 발생할 수 있습니다. * **정적 팩토리 함수**: 생성자를 `private`으로 숨기고 별도의 `createInstance` 같은 함수를 제공하면, 복잡한 준비 로직을 안전하게 처리한 뒤 완전한 상태의 인스턴스만 반환할 수 있습니다. **호출 시점에 실행되는 지연 초기화** 준비 작업의 비용이 크지만 실제로 사용되지 않을 가능성이 있을 때 유용한 방식입니다. * **최초 접근 시 실행**: 메서드 내부에서 준비 상태를 확인하고, 필요한 경우에만 로직을 실행하여 리소스를 효율적으로 관리합니다. * **Kotlin의 lazy 위임**: 수동으로 상태 체크 코드를 작성하는 대신 `by lazy`를 활용하면, 스레드 안전성을 확보하면서도 코드를 더 깔끔하게 유지할 수 있습니다. * **가변성 제어**: 수동 구현 시 속성을 가변(`var`)으로 선언해야 하는 단점이 있지만, `lazy`를 사용하면 이를 완화할 수 있습니다. **정적 타입을 활용한 상태 분리** 준비 전과 후의 상태를 별개의 클래스로 정의하여 컴파일 단계에서 오류를 방지하는 방식입니다. * **타입에 따른 권한 부여**: 준비 전 클래스에는 `prepare()`만 정의하고, 이 함수가 준비 완료된 새로운 타입의 객체를 반환하게 하여 `play()`와 같은 핵심 기능을 준비된 객체만 가질 수 있도록 제한합니다. * **컴파일 타임 안전성**: 호출자가 준비 과정을 거치지 않으면 기능을 아예 호출할 수 없으므로, 런타임 예외 발생 가능성을 원천적으로 차단합니다. * **세밀한 제어**: 호출자가 준비 시점을 직접 결정해야 하거나, 준비된 상태의 인스턴스를 캐싱하여 재사용해야 할 때 특히 효과적입니다. **실용적인 제언** 가장 좋은 설계는 사용자에게 주의를 요구하는 대신, 구조적으로 실수를 방지하는 설계입니다. 초기화 비용이 낮다면 생성자나 팩토리 함수를 통한 **즉시 초기화**를 권장하며, 실행 시점을 제어해야 하거나 안전성을 극대화해야 한다면 **상태별 클래스 분리**를 검토하는 것이 좋습니다.

line원문

코드 품질 개선 기법 17편: 사상누각 (새 탭에서 열림)

무분별한 빌더 패턴의 사용은 필수 인자의 누락을 런타임 시점에야 발견하게 만들어 코드의 안정성을 해칠 수 있습니다. 견고한 소프트웨어를 구축하기 위해서는 런타임 에러 대신 컴파일 타임에 결함을 발견할 수 있는 생성자나 팩토리 함수를 우선적으로 고려해야 합니다. 특별한 제약 사항이 있는 경우가 아니라면, 프로그래밍 언어의 기능을 활용해 불완전한 객체 생성을 원천 차단하는 것이 코드 품질 개선의 핵심입니다. **빌더 패턴의 한계와 위험성** * 전통적인 빌더 패턴은 필수 인자가 누락되어도 컴파일 단계에서 이를 감지하지 못하며, `build()` 호출 시점에 `IllegalStateException` 등의 런타임 에러를 발생시킨다. * 이는 '사상누각'처럼 기초가 불안정한 코드를 양산하는 결과를 초래하므로, 컴파일러가 인자 누락을 체크할 수 있는 생성자 기반 설계를 지향해야 한다. **기본값이 있는 인자가 많은 경우의 대안** * Kotlin과 같이 기본 인수를 지원하는 언어에서는 빌더 대신 생성자에 기본값을 설정함으로써 인자 전달의 유연성을 확보하고 가독성을 높일 수 있다. * 만약 환경상 빌더 패턴을 반드시 사용해야 한다면, 필수 인자만큼은 빌더의 생성자 인수로 직접 전달받도록 설계하여 누락 가능성을 구조적으로 방지한다. **생성 중인 상태의 처리와 타입 구분** * 빌더 객체를 다른 함수에 인자로 전달해 값을 채우는 방식(출력 인수)은 가독성을 떨어뜨리므로, 값을 반환받아 생성자나 팩토리 함수에 전달하는 방식으로 개선하는 것이 바람직하다. * 객체 생성 로직이 복잡한 파이프라인 형태라면 각 단계마다 서로 다른 타입을 정의함으로써, 유효하지 않은 중간 상태의 객체가 사용되는 것을 방지할 수 있다. **빌더 패턴이 효과적인 상황: 마지막 작업 정의** * 0회 이상 임의의 순서로 적용되는 작업이 있고, 특정 '마지막 작업(terminal operation)'을 통해 최종 결과를 산출해야 하는 경우에는 빌더 패턴과 유사한 구조가 유용하다. * 예를 들어 이미지 편집 과정(crop, filter 등)에서 데코레이터 패턴을 사용할 때, 빌더 형식을 도입하면 순수 데코레이터 패턴보다 중첩 구조가 단순해져 가독성이 크게 향상된다. 객체 생성 시 발생할 수 있는 결함을 런타임이 아닌 컴파일 타임에 검출할 수 있도록, 가장 먼저 생성자나 팩토리 함수 사용을 검토하세요. 빌더 패턴은 언어적 제약이 있거나 특수한 파이프라인 설계가 필요한 경우에만 선택적으로 활용하는 것이 좋습니다.

line원문

코드 품질 개선 기법 16편: 불이 'null'인 굴뚝에 연기가 'null'이 아닐 수 없다 (새 탭에서 열림)

널 객체(null object) 패턴은 `null` 대신 '비어 있음'을 나타내는 객체를 사용하여 호출부의 코드를 단순화하고 예외 처리를 줄이는 유용한 디자인 패턴입니다. 그러나 일반적인 상태와 오류 상태를 명확히 구분해야 하는 상황에서 이 패턴을 무분별하게 사용하면, 컴파일러의 정적 검증을 우회하게 되어 오히려 버그를 발견하기 어렵게 만듭니다. 따라서 오류 처리가 필수적인 로직에서는 널 객체 대신 언어 차원의 `null`이나 `Optional` 타입을 사용하여 타입 안정성을 확보하는 것이 권장됩니다. ### 널 객체 패턴의 활용과 장점 널 객체 패턴은 유효하지 않은 값이나 비어 있는 상태를 특정 객체로 정의하여 프로그램의 흐름을 끊지 않도록 돕습니다. - **코드 단순화**: 컬렉션의 경우 `null` 대신 빈 리스트(`.orEmpty()`)를 반환하면 호출 측에서 별도의 널 체크 없이 즉시 순회(iteration) 로직을 수행할 수 있습니다. - **폴백 데이터 제공**: UI 표시를 위한 데이터 모델에서 '알 수 없는 사용자'와 같은 기본 객체를 정의하면, 데이터가 없는 경우에도 화면 레이아웃을 깨뜨리지 않고 기본 정보를 안전하게 보여줄 수 있습니다. - **로직 통합**: 경계 조건이나 오류 상황을 일반적인 비즈니스 로직에 자연스럽게 통합시켜 코드의 가독성을 높입니다. ### 널 객체 패턴이 유발하는 타입 안정성 문제 오류 상태를 일반 객체처럼 취급하게 되면 개발자가 의도적으로 해당 상태를 확인해야 하는 로직을 누락했을 때 이를 잡아낼 방법이 부족해집니다. - **컴파일 타임 검증 부재**: `isInvalid`와 같은 속성으로 오류를 확인해야 하는 널 객체를 사용하면, 확인 로직을 잊더라도 컴파일러는 이를 정상적인 코드로 인식합니다. - **런타임 버그 발생**: 유효하지 않은 널 객체가 시스템 내부에서 계속 전달되다가 예상치 못한 지점에서 오작동을 일으킬 수 있으며, 이는 즉시 런타임 오류가 발생하는 것보다 원인 파악이 더 어렵습니다. - **대안으로서의 정적 타입**: Kotlin의 널 가능 타입(`?`)이나 Swift의 `Optional`을 사용하면 컴파일러가 강제로 널 처리를 요구하므로, 오류 조건과 일반 조건을 명확히 분리하여 처리할 수 있습니다. ### 널 객체 패턴 사용 시 주의할 점: 동일성과 동등성 널 객체를 정의하고 비교할 때는 객체의 비교 방식에 각별히 유의해야 합니다. - **동일성(Identity) 문제**: `UserModel.INVALID`와 같은 정적 인스턴스를 `==` 연산자로 비교할 때, 해당 클래스에 `equals`가 적절히 구현되어 있지 않으면 내용이 같더라도 다른 객체로 판별될 위험이 있습니다. - **값 기반 비교의 한계**: 단순히 기본값(ID 0, 빈 문자열 등)을 채워 넣은 새 객체를 생성해 비교할 경우, 실제 '무효한 상태'를 나타내는 싱글톤 객체와 일치하지 않아 로직 오류가 발생할 수 있습니다. ### 상황에 맞는 도구 선택 제안 널 객체 패턴은 만능 해결책이 아니며, 상황에 따라 적절한 도구를 선택하는 것이 중요합니다. - **널 객체를 권장하는 경우**: 일반적인 경우와 오류/경계 상황을 굳이 구분할 필요가 없거나, '오류'를 나타내는 후보가 너무 많아 정적 검증이 오히려 복잡해질 때 사용합니다. - **정적 타입을 권장하는 경우**: 비즈니스 로직상 오류 상태를 반드시 인지하고 별도의 처리(예: 에러 다이얼로그 표시)를 수행해야 한다면 널 객체 대신 언어에서 제공하는 `null`이나 `Optional`을 활용하여 타입 시스템의 보호를 받아야 합니다.

figma3분 읽기큐레이션 요약

Bulb, 디자인 시스템 Solar

Bulb는 여러 제품의 디자인·코드베이스·패턴이 서로 달랐던 문제를 해결하기 위해 Figma로 디자인 시스템 ‘Solar’를 구축했다. Figma의 브라우저 기반 협업과 팀 라이브러리를 활용해 디자이너뿐 아니라 개발자, 연구자, 콘텐츠 작성자, 이해관계자까지 디자인 과정에 참여하도록 만들었다. 핵심 전략은 제품 전반을 조사한 뒤 디자인 원칙을 세우고, 작고 단순한 컴포넌트와 패턴부터 점진적으로 표준화하는 것이었다. ## Bulb의 성장과 디자인 조직의 과제 - 영국의 친환경 에너지 기업 Bulb는 18개월 동안 2,000% 성장했다. - 디자인팀은 13명 규모로, 여러 제품 그룹에서 엔지니어·PM·리서처·콘텐츠 작성자와 협업했다. - 기존 5~6개 제품은 서로 다른 에이전시가 제작해 다음 문제가 있었다. - 제품마다 다른 코드베이스 사용 - 버튼, 체크박스, 드롭다운 등 디자인 패턴의 불일치 - 시각적 브랜드는 통일되어도 실제 사용 경험은 일관되지 않음 - 새롭게 구성된 디자인팀은 디자인 시스템과 이를 지원할 도구를 처음부터 설계할 수 있었다. ## Figma를 통한 개방적인 협업 - Bulb의 조직 문화는 사업 부문 간 협업과 투명성을 중시했으며, 디자인 도구도 이러한 문화를 지원해야 했다. - 프로젝트별 ‘팟(pod)’에 디자이너, 리서처, 콘텐츠 작성자, 개발자가 함께 참여했다. - Figma에서는 디자이너의 별도 허가 없이도 다른 직군이 파일을 열어 다음 작업을 수행할 수 있었다. - 디자인에 의견과 제안 남기기 - 최신 작업 내용 확인 - 아이디어 스케치와 브레인스토밍 - 프로토타입 제작 및 검토 - 개발자와 디자이너가 설계 과정 전반에서 함께 논의하며 가이드라인과 품질 기준을 확인할 수 있었다. - 비디자이너에게도 복잡하거나 위협적인 도구가 아니어서 사용자 리서처가 디자이너와 직접 아이디어를 발전시키기 쉬웠다. ## Solar 디자인 시스템의 구축 과정 - 목표는 여러 제품에서 일관된 디자인을 보장하는 ‘단일 진실 공급원(single source of truth)’을 만드는 것이었다. - Figma 팀 라이브러리를 활용하면 마스터 컴포넌트를 수정했을 때 이를 사용하는 여러 디자인에 변경 사항이 동기화됐다. - 모든 파일을 브라우저에서 접근할 수 있어 빠르게 움직이는 팀에 적합했다. - 구축 초기에는 Bulb의 각 제품 페이지를 스크린샷으로 수집해 Figma에 시각적 사이트맵을 만들었다. - 이 사이트맵을 통해 제품별 차이와 불일치를 한눈에 파악했다. - 여러 직군이 참여해 디자인 원칙을 정의하고, 이를 기준으로 컴포넌트와 디자인 패턴을 정리했다. - 버튼·체크박스·드롭다운처럼 여러 버전이 존재하던 요소를 검토해 더 단순하고 견고한 패턴을 선택했다. - 정리한 패턴은 8개 제품에 단계적으로 적용됐다. ## 작고 단순하게 유지하는 원칙 - 디자인 시스템은 처음부터 모든 상황을 포괄하려 하기보다 작은 범위에서 시작해야 한다. - Solar는 다음과 같이 복잡도를 의도적으로 낮췄다. - 제한된 색상 팔레트 - 적은 수의 디자인 패턴 - 관리하기 쉬운 단순한 구조 - 시스템이 복잡해질수록 유지보수와 운영이 어려워지므로, 필요한 요소만 포함하고 지속적으로 단순화하는 것이 중요하다. - 디자인 시스템을 여러 직군이 함께 만들면 실제 구현과 사용 맥락을 반영한 표준을 정립할 수 있다. Bulb의 사례는 디자인 시스템을 단순한 UI 컴포넌트 모음이 아니라, 디자인·개발·리서치·콘텐츠가 함께 일하는 협업 기반으로 구축해야 한다는 점을 보여준다. 먼저 제품 간 불일치를 시각화하고, 명확한 원칙을 정한 뒤, 작은 패턴부터 라이브러리화해 점진적으로 확장하는 접근이 실용적이다.

원문 읽기(새 탭에서 열림)
figma3분 읽기큐레이션 요약

이번 트위터 논란은

디자인 시스템은 아직 업계에서 하나의 고정된 정의로 합의되지 않은 개념이다. Airbnb의 Karri Saarinen은 이를 제품 전체의 디자인을 규정하는 “공유되고 통합된 원칙과 패턴의 집합”으로 설명하며, 규모가 큰 장기 프로젝트일수록 일관성과 제약이 필요하다고 주장한다. 글은 Saarinen의 트위터 논쟁을 통해 디자인 시스템의 의미와 범위가 여러 디자이너의 관점 속에서 형성되고 있음을 보여준다. ## 디자인 시스템 정의가 분명하지 않은 이유 - 디자이너마다 디자인 시스템을 다르게 이해한다. - UI 컴포넌트와 스타일 가이드의 모음으로 보는 관점 - 제품의 디자인 원칙과 패턴을 포함하는 체계로 보는 관점 - 조직 전체의 협업 방식과 의사결정 기준까지 포함하는 관점 - 2017년 당시에도 디자인 시스템은 비교적 새로운 개념이었으며, 업계 리더들조차 범위와 핵심 요소를 계속 정립하는 단계였다. - 따라서 디자인 시스템은 단순한 시각적 규칙집이라기보다, 제품을 일관되게 설계하기 위한 여러 원칙과 패턴의 통합된 체계로 논의되고 있다. ## 규모가 커질수록 필요한 제약과 일관성 - Saarinen은 대규모·장기 프로젝트가 성공하려면 디자인 시스템이 필요하다고 설명했다. - 여러 팀과 플랫폼이 동시에 제품을 개발할 때 디자인 시스템은 다음을 돕는다. - 화면과 기능 간 시각적 일관성 유지 - 반복적인 디자인 의사결정 감소 - 팀 간 협업 기준 통일 - 제품이 확장될 때 디자인 품질 유지 - 여기서 제약은 창의성을 제한하기 위한 것이 아니라, 팀이 매번 기본 요소를 새로 결정하지 않고 중요한 문제에 집중하게 하는 장치로 볼 수 있다. ## Airbnb의 디자인 시스템 구축 방향 - Saarinen은 Airbnb의 디자인 시스템을 만들면서 제품의 전반적인 경험을 하나의 언어로 통합하려 했다. - 그가 제시한 주요 원칙은 다음과 같다. - **Unified**: 제품 경험이 서로 연결되고 통일되어야 함 - **Universal**: 다양한 상황과 사용자에게 적용될 수 있어야 함 - **Iconic**: Airbnb만의 분명하고 기억에 남는 정체성을 가져야 함 - **Conversational**: 사용자와 자연스럽게 소통하는 경험을 제공해야 함 - 이 접근은 디자인 시스템을 색상, 버튼, 아이콘 같은 시각 요소의 목록으로 한정하지 않고, 제품이 사용자에게 전달하는 태도와 경험까지 포함한다. ## 트위터 논쟁이 보여준 관점의 다양성 - Saarinen이 자신의 최신 정의를 공개하면서 다른 디자인 리더들과 공개적인 논의가 시작됐다. - 그의 정의는 디자인 시스템을 다음과 같이 본다. - 제품 전체 디자인을 규정하는 원칙과 패턴 - 여러 팀이 공유하고 함께 사용하는 통합된 기준 - 개별 화면이 아니라 제품 전반의 경험을 다루는 구조 - Facebook의 제품 디자이너 Sean Blanton을 비롯한 업계 인사들의 반응은 디자인 시스템의 범위를 둘러싼 의견 차이를 드러냈다. - 글은 이 논쟁을 통해 정답 하나를 제시하기보다, 디자인 시스템의 핵심이 무엇인지 업계가 실시간으로 조정하고 구체화하는 과정을 보여준다. ## 디자인 시스템은 완성된 산출물이 아닌 evolving한 체계 - 디자인 시스템은 한 번 만들어 배포하면 끝나는 문서나 라이브러리가 아니다. - 제품, 조직, 플랫폼이 변하면 원칙과 패턴도 함께 조정되어야 한다. - 중요한 것은 특정 구성 요소의 개수보다 다음과 같은 통합성이다. - 디자인 원칙과 실제 UI 패턴의 연결 - 디자이너와 개발자가 공유하는 언어 - 여러 제품 영역에 걸친 일관된 사용자 경험 - 조직이 성장해도 유지되는 의사결정 기준 실무에서는 디자인 시스템을 단순한 컴포넌트 라이브러리로 시작하되, 색상·타이포그래피·레이아웃 같은 시각 규칙뿐 아니라 제품 원칙과 사용자 경험의 방향까지 함께 정의하는 것이 좋다. 또한 조직과 제품의 규모에 맞춰 범위를 정하고, 실제 팀의 사용과 피드백을 반영하며 지속적으로 발전시켜야 한다.

원문 읽기(새 탭에서 열림)