[인프런 워밍업 클럽 백엔드 스터디 2기] 1주차 발자국
학습 내용
리팩토링을 왜할까? 클린코드를 왜 작성할까?
우리는 생산을 해냈다고 끝이 아니다. 이후에 조상들이 작성한 코드를 후손들이 유지보수하는 일이 생기기 마련이다. 이에 관련하여 여러가지 방법에 대하여 배워보자
추상? 그게 뭔데
의미를 담을 수 있는 더 작은 단위, 책임
이름짓기
단수와 복수의 구분
관용어가 아닌 이상 줄임말 자제
그 집단만 알 수 있는 은어/방언 사용하지 않기
좋은 코드를 많이 보기
메서드로의 분리
의미를 담을 수 있는 더 작은 단위로 메서드를 분리하기
기준 : 추상화되는 의미가 있는가? 이미 이해가 잘 간다면 그대로 두는게 더 나을 수 있다.
추상화 레벨
구체에 가깝다 = 추상화 레벨이 낮다. (디테일한 코드 상세)
추상에 가깝다 = 추상화 레벨이 높다. (디테일한 코드를 외부세계로 감싸는 메서드)
하나의 세계 안에서는, 추상화 레벨이 동등해야 한다. 내가 너무 주변 레벨에 비해서 구체화시키고 있다면 추상화를 고려해보자
매직넘버와 매직스트링
의미를 갖고 있으나, 상수로 추출되지 않은 숫자상수를 추출하고 이름을 짓고 의미를 부여함으로서 가독성이 향상될 수 있다.
상수로 뺀다는건, 이거는 중요한 숫자야~ 유지보수할때 중요깊게 봐야할 필요가 있을거야~ 라는 의미를 줄 수 있다.
논리 사고의 흐름
#최소의 인지적 노력으로 최대의 정보를 제공
# 뇌는 한번에 한가지 일 밖에 하지 못한다. 멀티태스킹? 그건 저글링일 뿐이다.
뇌에 메모리를 적게 쓰게 하기.
Early return
논리의 흐름을 빠르게 중단시켜 생각 회로를 단순화 한다.
코드 흐름을 단순화하고 가독성을 향상
중첩분기, 중첩반복문에 대한 최적화
depth를 1로 만들 수 있는지 생각해보기
때로는 stream에 대한 활용도 고려할 수 있다.
2중 중첩구조 안에도 사고에도움이 된다면 중첩 안에 메서드를 분리하여 두어도 좋다.
사용할 변수는 가깝게 선언하기
공백라인도 의미를 가진다
복잡한 로직의 의미 단위를 나누어 보여줌으로써 읽는 사람에게 추가적인 정보를 전달할 수 있다.
부정어를 대하는 자세
논리를 거꾸로하여 생각하는 과정을 거친다는 것은 가독 저하의 요인일 수 있다.
부정어구를 쓰지 않아도 되는 상황인지 체크하기 (무조건 부정조건으로검사해야하는 상황인지)
부정의 의미를 담은 다른 단어자체가 존재하는지 고민하기 or 부정어구로 메서드 명 구성
해피케이스와 예외 처리
예외가 발생할 가능성 낮추기
검증로직은 생성자 or 별도의 분리가 필요
의도한 예외와 예상하지 못한 예외를 구분하기
Null을 대하는 자세
equals작성시 상수를 앞에 둔다.
Optional은 비싼 객체라 항상 좋은건 아니지만 값이 있을 수도 있고 없을 수도 있는 상황에는 고려할 수 있다. 이때 멤버변수에 Optional은 좋지 않다. 만들어진 목적이 메서드의 반환타입에 쓰게금 설계되어있다.
[orElse() : 괄호 안에 값이 항상 실행
orElseGet() : 옵셔널안에 원본값이 null인 경우 실행
orElseThrow() : 값이 있으면 쓰고 없으면 예외를 던지겠다. 라는 의미라서 그냥 써도된다.
StackTrace는 안티패턴이다.
추상의 관점으로 바라보는 객체 지향
객체지향 패러다임
객체 간의 협력과 객체가 담당하는 책임
객체간의 협력과 객체가 담당하는 책임의 관점으로 추상화를 접목시켜 이해하는 것이 중요하다.
관심사의 분리
관심사끼리 객체를 분리한다.
높은 응집도, 낮은 결합도 유지
객체 설계하기
객체도 추상화와 같이 객체로 나누는 순간 외부와 경계가 발생한다. 이때 외부세계와 소통을 위해서 공개메서드를 활용한다.
객체는 외부로 기능을 제공해준다. (개념의 가시화 = 관심사)
비공개필드(데이터), 비공개로직(기능 구현부), 공개 메서드 선언부만 외부로 노출하여 이루어진다.
여러 객체를 사용하는 입장에서는 구체적인 구현에 신경쓰지 않고 보다 높은 추상화 레벨에서 도메인 로직을 다룰 수 있다.
새로운 객체를 만들 때 주의할 점
1개의 관심사로 명확하게 책임이 정의되었는지 확인
유효성 검증
setter 사용 자제(데이터의 불변성)
getter가 꼭 필요한지 생각해보기 (객체에 메시지를 보내서 필요한 정보를 메서드로 가져올 수 없는지?)
필드의 수는 적을 수록 좋다.
객체지향 패러다임(SOLID)
SRP : ”책임을 볼 줄 아는 눈”
전문가가 되어야한다. 하나 이상의 일을 책임지게 되면 업무의 효율성이 떨어지게 된다. 이를 어기면 각각의 역할에 집중할 수 없고 공통 업무의 변화가 생기면 모두 바뀌어야한다.
OCP : "기존 코드의 변경 없이 , 시슽템의 기능을 확장할 수 있어야한다. (추상화와 다형성의 활용)"
전기콘센트 같은것. 허용 전압 규격만 맞으면 냉장고, 가스레인지 등 모든 가전제품을 사용 가능하다. 가전제품이 늘어나도 규격이 맞으면 사용 가능하고 전기콘센트를 교체할 필요 없다.
LSP : "자식클래스는 부모클래스의 책임을 준수하며, 부코클래스의 행동을 변경하지 않아야한다."
엄마말을 잘 듣자. 상속의 세계에서는 자식은 부모를 이길 수 없다. 따라서, 자식은 부모의 규칙을 어기거나 변경할 수 없이 말을 잘 들어야한다.
ISP : "인터페이스를 기능단위로 잘게 쪼개라"
덜어냄의 미학. 흑백요리사에서처럼 인터페이스도 많은 기능(책임)을 가진다고 좋은게 아니다. 필요한 만큼만 가지는게 최고다.
DIP : "상위 수준의 모듈은 하위 수준의 모듈에 의존해서는 안 된다. 둘 모두 추상화에 의존해야 한다."
수준에 맞게 놀자. 고수준(추상 : 핵심비지니스 로직) 모듈이 저수준(구체 : 구현세부사항)모듈을 직접적으로 참조하는 것은 저수준모듈의 변경에 민감하게 반응하게된다.
객체지향 적용하기
상속과 조합
상속보다는 조합을 사용하자
상속은 시멘트처럼 굳어진 구조다. (부모와 자식의 결합도가 높다.)
조합과 인터페이스를 활용하는 것이 유연한 구조다.
상속을 통한 코드의 중복 제거가 주는 이점보다, 중복이 생기더라도 유연한 구조 설계가 주는 이점이 더 크다.
상속 : extends → 부모 - 자식 간의 필수 오버라이딩 제약이 없고, 부모의 구현을 자식이 알고있는 관계 → 자식.부모메서드() 로 호출이 가능하여 재사용성이 향상됐다고 볼 수 있지만 객체 지향 관점에서는 캡슐화가 깨진 상태라고도 볼 수 있음
조합 : 부모에는 공통 스팩 제공하는 인터페이스를 사용하고, 해당 부모의 구현체들은 부모의 스팩만 유지한 상태라면 부모에 독립적이고 변경이 많아질 수 있는 부분은 부모 클래스에 두지 않고 별도의 클래스로 분리함으로써 캡슐화와 확장성을 지킨다.
주입 : 외부에서 부모 클래스나 자식 클래스가 의존하는 객체들을 주입함으로써, 객체 간의 강한 결합을 방지하고, 필요 시 구현체를 쉽게 교체할 수 있게 하여 유지보수와 테스트의 용이성을 높인다.
Value Object
도메인의 어떤 개념을 추상화하여 표현한 값 객체
값으로 취급하기 위해서, 불변성, 동등성, 유효성 검증 등을 보장해야 한다.
엔티티와 vo의 차이(식별자 유무 차이)
일급 컬렉션
일급시민이란? -> 다른 요소(누군가의 파라미터, 변수, 함수의 결과로 return)에게 사용 가능한 모든 연산을 지원하는 요소
일급컬렉션이란?
컬렉션을 포장하면서 필드로 무조건 해당 컬렉션 하나만을 가지고 있는 객체
컬렉션을 다른 객체와 동등한 레벨로 다루기 위함
getter로 컬렉션을 반환할 일이 생긴다면 필드에서 가지고 있는 컬렉션과 같은 참조를 리턴하는 일이 생기지 않도록 new해서 새로운 컬렉션을 반환하자.
enum의 특성과 활용
상수의 집합
변경이 정말 잦은 개념이면 enum보다 db로 관리하는게 좋을 수 있음.
다형성 활용하기
변하는 것과 변하지 않는 것을 분리하여 추상화하는 것.
변하지 않는 것을 지켜야[추상] → 변경에는 닫혀있는 상태를 유지 가능
변화하는 것에는 유동적으로 해결이 되어야[구체] → 확장에 열려있는 상태가 된다.
숨겨져 있는 도메인 개념 도출하기
완벽한 설계는 없다. 그 당시의 최선이 있을 뿐.
도메인 지식은 만드는 것이 아니라 발견하는 것
설계할 때는 근시적, 거시적 관점에서 최대한 미래를 예측하려는 시도가 필요하다
시간이 지나 만약 틀렸다는 것을 인지하면 언제든 돌아올 수 있도록 코드를 만들어야 한다.
미션
-> 주어진 코드에 대한 리팩토링
얼리리턴
추상화레벨 맞추기
처리해야할 예외와 그렇지 않은 예외의 분리
변수명, 함수명
책임의 분리
등.. 배운 지식을 활용하여 최적화하는 연습을 해보았다.
1주차 회고
Keep (만족했고, 앞으로도 지속하고 싶은 부분)
현재 코드를 작성하면서 "조상"과 "후손"이라는 표현을 사용한 점이 인상 깊었다. 이 표현은 코드의 지속성과 유지보수의 중요성을 잘 드러낸다고 생각이 들었다. 실무에서는 생산성과 클린 코드를 상반된 개념으로 보는 경우가 많은데, 이는 조직의 코드 문화에 긍정적이지 않다는 생각도 같이 들곤 했다. 이러한 문화를 개선하기 위한 방법론을 이번 학습을 통해 구체적으로 알게 되어 만족스러웠다..
Problem (아쉬웠던 점)
처음 보는 코드에 대한 빠른 이해가 부족하다는 점을 느꼈다. 즉, 현재 짜여진 코드를 너무 쉽게 납득해버리는 경향이 있는게 아닌가 싶다. 강사님처럼 코드를 보고 잠재적인 문제와 개선 가능성을 구조적으로 접근하는 시야가 필요하다고 생각한다. 이를 통해 "보는 눈"에 대한 중요성을 느꼈다.Try (다음에 시도해볼 점)
구체적인 코드 설계에서 추상과 구체를 명확하게 분리하는 연습을 해보고 싶다. 처음부터 완벽한 설계를 할 수는 없겠지만, 도메인의 책임을 분리하고 리팩토링을 고려한 설계를 시도해봐야겠다. 우선 흐름대로 코드를 작성한 후, 리팩토링을 통해 개선하는 과정을 반복하며 사고력을 기르는 과정을 연습해보아야 할 거 같다.
댓글을 작성해보세요.