[기술] 클린코드에 매몰되지 말자 (feat. 명확한 의도를 가지고 코드를 작성하자)

holyPigeon·2024년 11월 25일
1

기술

목록 보기
5/5
post-thumbnail

✋ Intro

최근 우테코 프리코스의 코드 리뷰를 진행하면서 유행하는 아키텍쳐저명한 개발자의 클린코드 원칙 등을 불문율처럼 따르는 사람들이 많다는 것을 느꼈다.

날것의 코드를 최대한 깨끗하게 리팩토링해나가는 것이 프리코스의 철학인데, 아무래도 클린코드 개념을 처음 접하는 사람들이 많다보니 자연스럽게 방법론에 의지하게 된 것 같아 안타까웠다.

나 같은 경우에는 갖은 시행착오 끝에 명확한 의도를 가지고 코드를 작성한다 라는 나름의 가치관을 정립할 수 있었다. 오늘은 이런 가치관을 갖게 된 이유, 그리고 그 과정 속에서 깨닫게 된 클린코드의 진정한 의미에 대해 공유해보려고 한다.

시행착오

클린코드 개념을 처음 접했을 때

내가 클린코드 개념을 처음 접했던 건 약 1년 전인 2023년 10월 쯤이었다.

사실 그 때도 올해와 같이 우테코 프리코스에 도전하고 있었고, 프리코스 내부의 여러 가이드 라인과 코드 리뷰들을 경험하면서 점점 클린코드에 대한 나만의 개념을 정립하게 됐다.

이 때 내가 가장 많이 참고했던 것이 바로 객체지향 생활 체조 원칙 이다.

당시 나는 매주 주어지는 미션들을 진행하면서 이미지 속 9개의 원칙들에 대해 하나하나 공부하고 적용해보는 시간을 가졌다.

공들여 적용하고 나니 코드가 확실히 깨끗해지는 느낌이 있었고, 코드 리뷰 상에서도 여러 긍정적인 피드백들을 받아서, 당시에는 하루하루 발전하는 느낌도 들고 뿌듯했던 것 같다.

허를 찌른 질문

그렇게 평소처럼 인터넷에 돌아다니며 온갖 객체지향 원칙과 방법론들을 적용해보던 어느 날, 코드 리뷰를 하다가 한 가지 질문을 받게 됐다.

이 부분은 왜 getter 메서드를 사용하지 않았나요? 사용했다면 훨씬 로직을 간편하게 짤 수 있었을 것 같아요.

당시 나는 getter 사용을 지양해야 한다 라는 객체지향 원칙을 과도하게 의식했기 때문에, 코드 대부분에서 getter를 제거했었고, 이로 인해 로직이 과도하게 복잡해진 상태였다.

뭉뚱그린 대답

"음... 캡슐화랑 OCP 원칙을 따르기 위해 getter를 쓰지 않았던 것 같아요..."

해당 질문에는 사실상 제대로 된 답변을 하지 못했다. 왜 이렇게 코드를 짰는지 에 대해 딱히 생각나는 게 없어서, 그저 특정 블로그에서 봤던 문장을 그대로 읊었을 뿐이었다.

사실 당연한 결과였던 게, 나는 문제가 생겨서 클린코드 원칙을 적용한 것이 아닌, 그냥 배운 내용을 무지성으로 코드에 때려박은 것뿐이었다. 캡슐화니 OCP니 말하긴 했지만, 사실 이것도 뚜렷하게 알고 말했던 건 아니었다.

분명 좋은 코드를 작성하기 위해 도입한 클린코드 원칙이었는데, 어느 순간부터 원칙 적용만을 목적으로 하다보니 도리어 코드가 더러워지는 상황이 발생하게 됐다. 주객전도가 돼버린 것이었다.

당위성을 찾아보자

그 날 이후부터는 반드시 코드를 이렇게 짜야 하는 이유가 있는가? 에 대해 깊게 고민하게 시작했다. 그 동안은 필요에 의해서 원칙을 찾아보고 적용해본 적이 없었는데, 비로소 정상적인 절차로 돌아오게 된 것이었다.

또한 어떤 방법론이나 아키텍처를 적용하던 간에, 항상 그게 꼭 필요한지 스스로에게 비판적인 질문을 던지곤 했는데, 예를 들자면 다음과 같은 식이었다.

'멀티쓰레드 환경을 생각해서 Synchronized 키워드를 붙여보는 건 어떨까?'

음... 멀티쓰레드 환경 대비하면 좋지. 근데 이거 로컬에서 돌리는 프로그램인데 굳이 그렇게까지 대비해야 되나..?

'헥사고날 아키텍처 요즘 많이들 쓰는데 한 번 적용해볼까?'

헥사고날이 뭐 확장성도 좋고 요즘 많이 쓰긴 하는데, 굳이 이렇게 조그만 프로그램에 적용하기엔 오버 엔지니어링 아닐까..?

만약 남들이 좋다는 기술들을 별 다른 고민 없이 모두 때려박았다면, 서비스 자체가 불필요하게 큰 규모를 가지게 됐을 뿐더러, 작성자의 전체적인 설계 의도를 파악하기 쉽지 않았을 것이다.

하지만 매번 위와 같은 엄격한 기준을 거쳐 기술을 적용하다보니, 기존에 있던 문제점들을 오버 엔지니어링 없이 1:1로 해결할 수 있게 되면서 적절한 규모를 갖추게 됐다.

이런 식으로 개발을 이어나가다 보니 점차 코드가 당위성을 가질 수 있었고, 사람들과 리뷰를 하면서도 왜 이렇게 코드를 작성했는가? 에 대해 자신있게 설명할 수 있게 됐다.

코드에 의도를 담아보자

여기까지 다다르면서 한 가지 뼈져리게 느꼈던 것은, 특정한 의도를 가지고 코드를 짜는 것이 매우 중요하다는 것이었다.

개발을 할 때는 하나의 기능을 구현할 때에도 여러가지 방법이 존재하는데, 확실한 의도를 갖고 있어야 서비스에 가장 적합한 방법을 찾을 수 있고, 추후 리뷰어들에게도 합당한 이유를 들어 설명할 수 있다.

예를 들어 사용자 입력값에 대한 검증 로직을 작성한다고 가정해보자.
다음과 같이 도메인 내에서 검증을 진행할 수도 있고,

public class Member {
	private Long id;
	private String name;
    
    ...
    
    public void validateName(String name) {
    	if (name.isEmpty()) {
        	throw new IllegalArgumentException("이름이 비어있습니다.");
        }
    }
}

이렇게 별도의 검증 클래스를 구현하여 Service 레이어에서 검증을 진행할 수도 있다.

public class MemberValidator {
	public void validateName(String name) {
    	if (name.isEmpty()) {
        	throw new IllegalArgumentException("이름이 비어있습니다.");
        }
    }
}

public class MemberService {
    ...

	public void join(MemberJoinRequest request) {
    	memberValidator.validateName(request.getName());
    	Member member = new Member(request.getName());
        memberRepository.save(member);
    }
}

만약 도메인 주도적인 설계를 가저가고 싶다면 전자를, 보다 유연하고 세밀한 검증 규칙을 가져가고 싶다면 후자를 적용할 수 있을 것이다.

개발을 하다보면 이렇듯 반드시 이것이 정답이다 라고 확언하지 못하는 상황이 정말 많은데, 이 때마다 본인의 의도, 서비스의 목적 등을 잘 상기해야 최적의 방안을 선택할 수 있겠다는 것을 느꼈다.

결론

결국 개발자에게 은탄환(Silver Bullet)은 없다 라는 말처럼, 코드를 짜는 데 정답 같은 건 없는 것 같다.

그 유명한 클린코드 원칙들도 적합한 상황에서 사용했을 때 빛을 발하는 것이지, 앞뒤 안 가리고 사용하다간 끝내 그럴듯한 스파게티 코드가 돼버릴 수 있다.

클린코드 자체에 매몰되지 않고 그 본질에 다가가기 위해선, 단편적인 한 줄의 문장에만 집착하기 보단 전체적인 맥락과 큰 그림을 보는 것이 중요한 것 같다.

만약 이 글을 보는 주니어 개발자들이 있다면, 책 한 권만 읽은 사람이 제일 무섭다 라는 말을 기억해주면 좋겠다. 모두들 다양한 경험을 통해 넓은 시야를 키우고, 자신만의 코드 작성 기준을 세워 나가길 바라면서 오늘의 포스팅을 마치도록 하겠다.

👍

profile
언젠가 전설이 될 남자... 피존입니다.

0개의 댓글