Notice
Recent Posts
Recent Comments
Link
«   2024/09   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30
Tags
more
Archives
Today
Total
관리 메뉴

DevYGwan

"좋은 코드"의 작성 기술 본문

Study/JAVA

"좋은 코드"의 작성 기술

YGwan 2024. 1. 22. 20:50

 

 

코드를 짜다보면 항상 이런 생각이 듭니다.

 

좋은 코드란 무엇일까?
객체 지향적으로 코드를 짜려면 어떻게 짜는 것이 좋을까?

 

 좋은 코드는 코드의 양이 많아질때 위력을 발휘한다고 생각합니다. 코드의 양이 적고 나 혼자 작업한다면, 사실 단순히 동작하는 코드가 중요하지 좋은 코드를 작성하는 것이 많이 중요하지는 않다고 생각합니다. 하지만 코드의 양이 많아지고 남들과 협업하는 환경에서 단순히 기능만을 위한 코드를 작성한다면 이건 이기적인 마음이라고 생각합니다. 다른 사람이 내가 작성한 코드를 보고 이해하고 코드를 추가 및 수정해야되는데, 코드를 봤을때 해당 코드가 너무 지저분하다면, 코드를 이해하는데 너무 많은 시간을 사용해야 하기 때문입니다. 실제로 내가 코드를 작성하는 것보다 남의 코드를 보고 이해하는게 더 어려운 경험이 있는데, 그때마다 "아 그냥 내가 새로 다시 작성할까?" 라는 고민이 많이 든 경험이 있을 것입니다...

 

그렇다면 좋은 코드란 무엇일까요? 제가 생각하는 좋은 코드란 이렇게 정의할 수 있을 것 같습니다.

 

●   성능 뿐만 아니라 가독성이 좋은 코드

●   주석 없이도 충분히 다른 사람이 해당 코드를 이해하는데 많은 시간이 들지 않는 코드

●   중복이 없고 불필요한 코드의 부분이 없는 코드

●   오류 없이 정확하게 원하는 기능을 수행하는 코드

 

 

라고 생각합니다. 코드란 결국 "개발자들의 말 & 행동" 이라고 생각합니다. 다른 사람들과 대화할때 다른 사람이 내 말을 이해하기 힘들다면 의사소통하는데 문제가 발생할 것입니다. 또한, 내가 전달하고 싶은 바가 제대로 전달되지 않는다면 그 또한 문제를 발생시킬 것입니다. 이러한 문제를 해결하기 위해선 제대로 & 잘 전달해야겠죠!! 그렇다면, 어떻게 좋은 코드를 작성할 수 있는지 제가 생각하는 팁에 대해서 제가 생각하는 방법을 정리해보도록 하겠습니다.

 

 

 


 

 

 

내가 생각하는 좋은 코드 작성 방법

 

 

 

1.  코드 작성 전에 항상 코드 컨벤션을 먼저 정의하자

 

코드를 작성하기 전 코드 컨벤션을 미리 정의해서 이를 기반으로 코드를 작성하자!

주로 사용되는 메서드 & 변수명, 코드 규칙, 지양해야할 로직 등을 미리 컨벤션으로 정의해놓고 이를 기반으로 작성하면, 네이밍 등에 고민할 시간도 줄어들고 다른 팀원도 해당 로직을 바로 이해하기 편하고 수정하기에도 편하기 때문입니다.

 

 

2.  의미 있는 네이밍을 사용하자

 

변수명 & 함수명을 지을때 바로 해당 역할을 알 수 있도록 네이밍을 하는 것이 중요합니다. 보편적이고 특이한 네이밍은 지양하고 검색하기 쉽고 의미 있는 네이밍을 하는 것이 중요합니다. 변수명에 자료형을 넣는 것 또한 별로라고 생각합니다. 개인적으로 너무 긴 변수명은 별로라 적당한 길이를 가진 변수명으로 네이밍을 하는 편입니다. 2 ~ 3개 글자를 혼합한 변수명 정도가 적당하다고 생각합니다. 이전에 코드 컨벤션을 통해 그 규칙에 맞는 네이밍 방식이 있다면 이를 따라가는 것이 가장 좋은 방법이라고 생각합니다.

  • 클래스 이름 & 객체 이름 → 명사 & 명사구
  • 메소드 이름 → 동사 동사구

 

 

3.  함수와 변수는 한가지 일만 처리하도록 구현하자

 

객체 지향 SOLID 원칙 중 S(Single Responsibility Principle)입니다. 함수와 변수는 한가지 일만 처리하도록 로직을 구현하는 것이 중요합니다. 한 메서드나 변수는 하나의 일만 처리하도록 구현해야하며, 만약 여러 로직을 구현하게 된다면 이를 메서드로 분리해서 처리하는 것이 가독성 및 유지 보수성이 좋은 코드가 됩니다.

 

 

4.  함수의 매개변수는 너무 많은 것은 지양하자

 

매개변수가 너무 많으면 오히려 매개변수가 어떤 것인지 파악하는데 많은 시간이 걸리고 만약 코드를 작성하다가 매개변수의 순서라도 잘못 입력하게 되면 큰 문제가 발생합니다. (만약 자료형이 같은 매개변수라면) 이러한 문제는 컴파일 단계에서 오류를 확인할 수 없기 때문에  오류 찾기가 힘듭니다. 따라서 저같은 경우에는 매개변수가 너무 많은 함수가 존재하면 제가 과연 함수를 제대로 쪼갠 것이 맞는지 한번 더 확인합니다. 물론 한 함수가 매개변수가 많이 필요한 것은 어쩔 수 없지만 대부분은 그 함수가 너무 많은 역할을 수행하기 때문에 매개변수가 많아지는 경우가 많기 때문입니다. 따라서 저같은 경우에는 매개변수가 너무 많은 함수가 있다면, 해당 함수가 한가지 역할만 수행하고 있는지 확인하고 분리하기 위해 노력하거나 데이터 클래스를 생성해 처리하는 편입니다. 함수의 매개변수도 클래스 하나로 처리가 가능하고 데이터들 간의 로직들 또한 이 클래스에서 처리한다면 코드의 가독성 및 유지보수성을 향상시킬 수 있기 때문입니다.

 

 

5.  주석 대신 코드로 의도를 표현하자

 

너무 많은 주석은 오히려 가독성을 방해한다고 생각합니다. 코드 중간 중간에 글이 많다면, 코드가 길어져 오히려 방해가 되기 때문입니다. 또한, 주석은 그 코드 작성 당시에 내용만을 반영합니다. 하지만 코드는 계속해서 변화하고 발전됩니다. 만약 주석과 코드의 싱크가 맞지 않게 된다면 코드 이해를 더 어렵게 만들 수 있습니다. 한 책에서 주석을 표현한 용어 하나를 공유하려고 합니다. 그 책에서는 주석을

"주석은 내가 명확하게 코드로 의도를 표현하지 못할 것 같은 경우에 사용하는 것이다."

 

라고 소개했습니다. 코드로 의도를 충분히 표현할 수 있다면 주석은 불 필요한 것이기 때문입니다. 하지만 중요한 주석 또한 존재합니다. 중요한 주석의 예를 간단히 소개하자면,

  • 코드가 복잡할 경우 간단하게 해당 코드가 어떤 코드인지 밝히는 주석
  • 해당 코드로 인해 발생할 수 있는 문제를 경고하는 주석
  • 버전 정보 등을 간단하게 밝히는 주석
  • 추가적으로 구현해야 할 일을 주석으로 밝히기  (Todo 사용)

위의 주석은 과하지 않다면 필요한 주석들 중 하나라고 생각합니다. 따라서 주석을 사용할때는 코드적으로 더 확실하게 표현할 수 있는 방법이 있는지 확인하는 습관을 갖는 것이 좋다고 생각합니다.

 

 

6.  중복 코드 작성을 지양하자

 

중복되는 코드는 최대한 지양하는 것이 좋습니다. 관리 포인트가 많아지기 때문입니다. 초반에 구현할때는 문제가 없을 수 있지만 이후에 만약 코드가 변경되는 상황에서 변경이 한쪽만 이루어진다면, 이에 대한 에러 확인 및 수정이 어려울 수 있기 때문에 이러한 중복 코드는 되도록이면 지양하는 것이 좋습니다. 따라서 당장은 문제가 없을 수 있어도 이후에 문제를 야기할 수 있기 때문에 중복 코드는 최대한 지양하는 것이 좋습니다. 저같은 경우에는 되도록이면 메서드로 빼는 편이고 매개변수나 응답값이 달라 비슷한 로직을 처리해야할 경우에는 자바의 다형성을 통해 이를 구현하는 편입니다.

 

 

7.  접근 제한자 고려와 final 키워드는 생활화하자

 

데이터는 최대한 접근할 수 있는 범위를 최소화하는 것이 중요하다고 생각합니다. 해당 데이터에 접근할 수 있는 범위가 넓다면 이것이 다 관리 포인트이기 때문입니다. 데이터의 유형에 맞게 접근 제한자를 설정해야 오류가 생겼을 경우 빠르게 오류 지점을 파악할 수 있을 것입니다. 또한 특정 데이터가 한번 값이 정해지면 수정되면 안되는 데이터면 final 키워드를 추가하는 것이 중요합니다. 코드가 길어지면 중간에 다른 팀원이 실수로 수정되지 않아야 할 데이터를 수정할 수도 있습니다. 그렇게 되면 큰 문제가 발생하기 때문에 이를 사전에 방지하기 위해 final 키워드를 추가하는 것 또한 중요합니다.

 

 

8.  NULL 반환 & 전달은 최대한 지양하자

 

NULL은 NPE(Null Point Exception) 에러를 발생시킬 수 있습니다. 따라서 되도록이면 NULL을 반환하거나 전달하는 것은 지양하는 것이 좋습니다. 빈 문자열, 빈 배열 / 리스트 등을 전달하는 것이 더 좋다고 생각합니다.

 

 

9.  if - else 문 보다는 Early Return을 통해 로직을 구현하자

 

if, else - if, else 문을 사용하는 것은 오히려 코드의 가독성을 해친다고 생각합니다. 실제로 클린 코드에서도 if - else문을 지양하라는 말이 있습니다. 저는 개인적으로 if else문은 "조건에 대한 처리" 만을 중점으로 가져가는 것이라고 생각합니다. 이 말은 다른 말로,  "조건에 대한 값을 리턴" 한다 라고 이해할 수 있습니다. 조건이 많아질때, if문을 통해 바로 return한다면 그 이후에 로직은 아에 신경을 안써도 된다라는 것이 더 확실하게 표현되기 때문에 더 깔끔한 코드가 될 수 있다고 생각합니다. 또한 { } { } 계속해서 나오지 않기 때문에 더 코드가 깔끔해질 수 있다고 생각합니다. 

 

 

10.  부정을 처리하기 보다는 긍정을 처리해 로직을 구현하자

 

부정을 처리하기 위해 사용하는 부정 연산자 !는 잘 안보입니다. if문에서 사용하거나 boolean값을 리턴하는 메서드 앞에 부정 연산자를 사용해서 로직을 처리하는 것은 생각보다 한눈에 보기 어렵습니다. 이럴 경우 if문에 차라리 긍정 부분을 선처리 하거나, 메서드 리턴값 자체를 변경하는 것이 더 가독성 높은 코드를 작성할 수 있다고 생각합니다. 물론 java 자료형에서 제공하는 메서드를 사용해서 처리하는 경우 리턴 형식 자체를 변경할 수 없기 때문에 부정 연산자를 사용해야 할 경우가 있습니다. 그 외에는 되도록이면 부정에 대한 처리보다는 긍정에 대한 처리가 더 좋다고 생각합니다.

 

 

11.  try catch를 사용할 경우 안의 로직이 길다면 별도 함수로 처리하자

 

try catch문 같은 경우 따로 메서드를 분리해 처리하는 것이 코드를 읽기 좋을 수 있습니다. try 안의 로직과 catch 안의 에러 핸들링 로직이 길어지면 코드를 한눈에 보기 힘들 수 있습니다. 따라서 try 안의 로직을 메서드로 분리하고 catch안의 로직을 메서드로 분리해놓는다면, 한눈에 "아 이건 어떤 로직이고 이때 발생하는 에러는 이런게 있고 이를 이렇게 핸들링하네?" 를 더 확실하게 볼 수 있어 코드의 가독성이 높아진다고 생각합니다.

 

 

12.  메서드 내 너무 많은 들여쓰기는 지양하자 (메서드로 빼기)

 

들여쓰기가 너무 많게 되면 코드가 지저분해질 수 있다고 생각합니다. 들여쓰기 안의 내용이 복잡해지고 들여쓰기 중첩도 많아지면 코드를 읽기가 너무 어려워집니다. 들여쓰기를 사는 상황은 for문과 if문이 있을 수 있겠죠. 저같은 경우 되도록이면 들여쓰기의 중첩이 필요하다면, 이를 메서드로 빼고 들여쓰기는 한개만 처리하는 것을 좋아합니다. 그렇게 되면 좀 더 코드가 깔끔해지는 것 같다는 생각이 들어서 들여쓰기의 중첩은 최대한으로 메서드로 빼는 것을 좋아합니다.

 

 

13.  변수명 만으로 자료형을 유추할 수 있다면, var를 사용하자

 

저는 요즘 var 키워드를 많이 사용합니다. 자료형이 그렇게 중요하지 않고 변수명만으로 자료형을 유추할 수 있다면, var를 쓰는 것이 코드의 가독성을 더 높여줄 수 있다고 생각합니다. 개인적으로 dto같은 클래스는 클래스명이 길고 자료형 자체가 의미가 그렇게 있지 않다고 생각합니다. 이런 자료형에 var를 쓴다면 코드가 깔끔해져 선호하는 편입니다.

 

 

14.  Enum을 사용하자

 

Enum을 사용해 데이터를 하나의 집합으로 묶으면 가독성이 좋아지고 컴파일 시점에 타입 체크가 가능해 에러를 사전에 방지할 수 있습니다. 상수들을 의미 있는 그룹으로 묶을 수 있으며, 코드에서 해당 상수를 사용할 때 명확하고 직관적인 표현이 가능합니다. 이를 통해 코드의 이해와 유지보수를 용이하게 만들어줍니다.

 

 

15.  유효성 검사는 최대한 자주 하자

 

유효성 검사는 최대한 잘게 하는 것이 코드를 유지보수하는데 더 좋습니다. 그래야 에러 포인트를 더 빠르게 캐치할 수 있고 예상치 못한 오류를 방지할 수 있습니다. 또한 사전에 불필요한 처리를 하는 것을 막을 수 있습니다. (데이터가 유효하지 않으면 이후 로직을 아에 실행하지 않기 때문에)

 

 

 


 

 

 

이렇게, 제가 생각하는 좋은 코드 작성법에 대해서 정리해보았습니다. 작성하다보니 좋은 코드 작성법은 좋지 않은 코드 작성법을 피하는 것이 좋은 코드를 작성하는 방법이라는 생각이 들었습니다. 생각보다 정리하다보니, 지양이라는 단어를 많이 쓰게 되었기 때문입니다. 물론 지양하는 것 말고도 추가적으로 해야하는 것 또한 많긴 했지만요!

 저의 방법이 꼭 정답이 아니고 제가 정리한 것은 모든 상황에 맞는 것은 아니라고 생각합니다. 제가 코드를 짜면서 고민하고 생각한 방식이고 이것보다 더 중요하고 좋은 방법은 많다고 생각합니다. 한번쯤 자신이 생각하는 좋은 코드의 기준과 작성법을 정리해본다면 이후에 코드를 짜는데 좀 도움이 되지 않을까 생각합니다.

이번에 정리하게 되면서 "방어적 프로그래밍" 이라는 단어를 알게 되었습니다.

 

방어적 프로그래밍(Defensive programming)이란 예상치 못한 입력에도 한 소프트웨어가 계속적 기능 수행을 보장할 수 있도록 고안된 방어적 설계의 한 형태입니다. (위키백과)

 

기능을 구현하고 이를 실행해보면 예상하지 못했던 에러가 발생하는 경우가 많습니다. 따라서 애초에 기능을 구현할때 최대한 발생가능한 에러들을 확인해서 이를 처리하는 것이 중요합니다. 그래야 예상하지 못한 에러를 최대한 줄일 수 있기 때문입니다. 이러한 방어적 프로그래밍을 생활화한다면 서비스의 큰 장애를 대비할 수 있을 것이라고 생각합니다.

마지막으로 여태까지 정리한 방법을 한번에 정리하고 객체 지향하면 자주 등장하는 객체 지향 5대 SOLID 원칙과 객체 지향 생활 체조 원칙 9가지를 정리하는 것을 끝으로 이번 글을 마치도록 하겠습니다.

 

 

 

 


 

 

좋은 코드 작성 팁

 

    ● TIP 1 :  코드 작성 전에 항상 코드 컨벤션을 먼저 정의하자

 

    ● TIP 2 :  의미 있는 네이밍을 사용하자

 

    ● TIP 3 :  함수와 변수는 한가지 일만 처리하도록 구현하자

 

    ● TIP 4 :  함수의 매개변수는 너무 많은 것은 지양하자

 

    ● TIP 5 :  주석 대신 코드로 의도를 표현하자

 

    ● TIP 6 :  중복 코드 작성을 지양하자

 

    ● TIP 7 :  접근 제한자 고려와 final 키워드는 생활화하자

 

    ● TIP 8:  NULL 반환 & 전달은 최대한 지양하자

 

    ● TIP 9 :  if - else 문 보다는 Early Return을 통해 로직을 구현하자

 

    ● TIP 10 :  부정을 처리하기 보다는 긍정을 처리해 로직을 구현하자

 

    ● TIP 11 :  try catch를 사용할 경우 안의 로직을 별도 함수로 처리하자

 

    ● TIP 12 :  메서드 내 너무 많은 들여쓰기는 지양하자 (메서드로 빼기)

 

    ● TIP 13 :  변수명 만으로 자료형을 유추할 수 있다면, var를 사용하자

 

    ● TIP 14 :  Enum을 사용하자

 

    ● TIP 15 :  유효성 검사는 최대한 자주 하자

 

 

 


 

 

SOLID 원칙

 

    ● S(SRP) : 단일 책임 원칙 -  클래스(객체)는 단 하나의 책임(기능)만을 담당해야 한다.

 

    ● O(OCP) : 개방 폐쇄 원칙 - 확장에는 열려있어야 하고, 수정에는 닫혀있어야 한다.

 

    ● L(LSP) : 리스코프 치환 원칙 - 상위 타입의 객체를 하위 타입의 객체로 치환해도 정상 작동해야한다.

 

    ● I (ISP) : 인터페이스 분리 원칙 - 하나의 일반적인 인터페이스보다는 여러개의 구체적인 인터페이스를 사용해라

 

    ● D(DIP) : 의존 역전 원칙 - 의존 관계를 맺을때는 추상화된 상위 개념을 사용하는 것이 좋다.

 

 

 

객체 지향 생활 체조 9 원칙

 

    ● 규칙 1 : 한 메서드에 오직 한 단계의 들여쓰기만 한다.

 

    ● 규칙 2 : else 예약어를 쓰지 않는다.

 

    ● 규칙 3 : 모든 원시값과 문자열을 포장한다.

 

    ● 규칙 4 : 한줄에 점을 하나만 찍는다.

 

    ● 규칙 5 : 줄여쓰지 않는다.(축약 금지)

 

    ● 규칙 6 : 모든 엔티티를 작게 유지한다.

 

    ● 규칙 7 : 3개 이상의 인스턴스 변수를 가진 클래스를 쓰지 않는다.

 

    ● 규칙 8 : 일급 콜렉션을 쓴다.

 

    ● 규칙 9 : Getter/Setter/Property를 쓰지 않는다.

 

 

 


 

 

참고 자료 :

 

 

'Study > JAVA' 카테고리의 다른 글

JAVA 특징 정리  (0) 2023.01.04
ArrayList<>(), List.of(), Arrays.asList() 차이점  (0) 2022.11.16