인프런 워밍업 스터디 클럽 2기 백엔드(클린코드&테스트코드) 발자국 - 1주차
인프런 워밍업 클럽 2기, 백엔드(클린코드&테스트코드) 과정에 참여하고 있습니다.이번 1주차에는 클린 코드를 추구하는 이유와 클린한 코드를 작성하는 방법, 객체 지향적 사고, 객체 지향으로 프로젝트를 리팩토링하는 디테일한 스킬을 예제와 함께 배웠습니다.강의 링크: Readable Code: 읽기 좋은 코드를 작성하는 사고법 [학습내용 핵심 요약]자세한 설명이나 예제 및 코드는 강의 저작권 보호를 위해 기재하지 않았습니다. 1. 추상클린 코드의 목적: 가독성 향상, 유지보수 용이성 증대, 시간과 자원 절약추상화: 중요 정보 추출, 불필요 정보 생략하는 과정컴퓨터에서 추상화: 복잡한 데이터와 로직 단순화, 이해도 향상적절한 추상화: 도메인 문맥 고려, 핵심 개념만 표현이름 짓기: 추상적 사고 기반, 도메인 문맥 내 이해 가능한 용어 사용메서드 추상화: 단일 책임 원칙, 명확한 의도 전달메서드 선언부: 적절한 메서드명, 파라미터, 반환 타입 선택추상화 레벨: 동일한 추상화 수준 유지, 일관성 있는 코드 작성매직 넘버/스트링 제거: 상수 추출로 의미 부여, 가독성 향상좋은 코드 습득: 업계 표준 용어 학습, 지속적인 개선 2. 논리, 사고의 흐름뇌 메모리 효율화: 최소 인지 노력으로 최대 정보 제공범주화와 인지적 경제성: 효율적 정보 처리 위한 핵심 전략Early return: 코드 가독성 향상, 불필요한 중첩 구조 제거사고의 depth 줄이기: 반복문, 조건문 구조 단순화, 변수 근접 선언부정어 사용 주의: 긍정 표현 선호, 메서드명에 부정 의미 포함 고려해피 케이스와 예외 처리: 예외 발생 가능성 최소화, 의도적/비의도적 예외 구분Null 처리: NullPointerException 방지, Optional 활용 고려Optional 사용 지침: 반환 타입에만 사용, 파라미터로 받지 않기, 빠른 해소Optional 해소 방법: 풍부한 API 활용 (orElseGet, orElseThrow, ifPresent 등)orElse, orElseGet, orElseThrow 차이 이해: 실행 시점과 용도 구분즉시 평가와 지연 평가 개념 숙지: Optional 메서드 사용 시 고려 3. 객체 지향 패러다임객체 지향: 추상화된 데이터와 코드의 결합, 객체 간 협력과 책임 중시관심사의 분리: 높은 응집도, 낮은 결합도 추구객체 설계: 비공개 필드/로직, 공개 메서드로 외부와 소통객체 생성 주의점: 단일 책임, 생성자에서 유효성 검증, setter 자제getter 사용 최소화: 객체에 메시지 전송 권장필드 수 최소화: 복잡도 감소, 불필요한 데이터 제거SOLID 원칙: 객체 지향 설계의 기본 원칙들SRP: 단일 책임 원칙, 변경 이유 하나로 제한OCP: 확장에는 열려있고, 수정에는 닫혀있어야 함LSP: 상속 관계에서 하위 클래스가 상위 클래스를 대체 가능해야 함ISP: 인터페이스 분리, 불필요한 의존성 제거DIP: 추상화에 의존, 의존성 역전을 통한 유연성 확보도메인 지식: 만드는 것이 아닌 발견하는 것의존성 주입(DI)과 제어의 역전(IoC) 개념 이해 4. 객체 지향 적용하기상속보다 조합 선호: 유연성 증가, 결합도 감소Value Object: 도메인 개념 추상화, 불변성/동등성/유효성 보장VO vs Entity: 식별자 유무와 동등성 판단 기준 차이일급 컬렉션: 컬렉션을 포장한 객체, 가공 로직 포함 가능Enum 활용: 상수 집합과 관련 로직 관리, 도메인 개념 명시적 표현다형성 활용: 반복적 if문 단순화, OCP 원칙 적용변하는 것과 변하지 않는 것 분리: 추상화와 구체화 구분도메인 개념 도출: 발견하는 과정, 현실 세계 흉내내기설계 접근: 근시적/거시적 관점에서 미래 예측유연한 코드 작성: 변경 가능성 고려, 재설계 용이성 확보완벽한 설계 없음: 현 시점 최선의 설계 추구일급 시민 개념: 변수 할당, 파라미터 전달, 함수 반환 가능컬렉션 반환 시 주의: 새로운 컬렉션으로 복사하여 반환Enum vs DB: 변경 빈도에 따른 선택객체 지향 설계: 현실 세계 완벽 반영이 아닌 모방 [고민했던 점]섹션 5. 객체지향 적용하기 중 CellPosition 을 분리하는 부분에 대한 강의를 들을 때, 강사님이 치고 계신 코드가 Minesweeper 파일인지 GameBoard 파일인지 헷갈릴때가 많았습니다. 강의를 멈추고 코드를 다시 읽어보다보니, 어떤 클래스에 들어있는지 한눈에 안들어오거나, 위치가 기억이 잘 안나거나 헷갈리는 메서드가 꽤 많았다고 느꼈습니다.Cell 이나, GameLevel, Handler 는 명확한 느낌을 받았습니다. Cell 안에 들어있는 메서드는 명확하게 Cell 에 대한 update 또는 get 동작을 수행하도록 설계되어 있다고 느꼈기 때문입니다.그런데 제 머릿속에는 "지뢰찾기" 라는 게임 그 자체에 "Board" 라는 개념이 항상 함께하는데, 그 "Board"를 Minesweeper 가 아닌 다른 파일로 쪼개놓았기 때문에 메서드의 위치가 헷갈린다는 느낌을 받았습니다. 제가 생각하기에는, GameBoard 라는 클래스가 일반적인 Board 기반 게임이 아닌, "지뢰찾기"에 특화된 로직을 포함하고 있기 때문에 헷갈린다는 느낌이 강하게 드는 것 같습니다. 예를 들어, initialize() 메서드 내부에는 Cell을 지뢰 칸으로 설정하는 로직과 숫자 칸으로 설정하는 로직이 녹아들어 있습니다. "지뢰찾기"만을 위한 로직이 녹아있는 것입니다. 그렇다면..?1. 추가적인 추상화?처음에는 GameBoard 내의 지뢰찾기 관련 로직을 별도의 클래스로 분리하는 것을 고려했습니다. 하지만 이는 "이른 최적화"의 함정에 빠질 수 있다는 생각이 들었습니다.2. 현실적인 접근현재로서는 이 GameBoard를 다른 보드 게임에 재사용할 계획이 없습니다. 따라서 과도한 추상화는 오히려 코드의 가독성을 해칠 수 있다고 판단했습니다. 결론지금으로서는 가장 좋은 방법은 GameBoard 라는 클래스 이름을 MinesweeperBoard 라는 이름으로 바꾸는게 좀 더 낫지않을까? 라는 결론을 내리는것이 가장 최선의 선택이 아닌가 싶습니다. 애초에 GameBoard 라는 이름이 추상적인 "Game"으로 시작하기 때문에, initialize 내부에 지뢰찾기와 관련된 로직이 녹아있는 것이 어색하게 느껴진것 아닌가? 라는 생각입니다. 그래서 클래스의 이름을 좀 더 구체적으로 변경하면, 비교적 덜 헷갈리지 않을까? 라는 생각입니다. 이상으로, 1주차 학습 내용 요약과 강의를 들으며 고민해봤던 내용을 정리한 발자국 포스팅을 마칩니다.