예측 가능한 코드를 작성하라-1

tk_jang·2023년 6월 5일

좋은코드 나쁜코드

목록 보기
8/20

매직값을 반환하지 말아야 한다.

매직값은 함수의 정상적인 반환 유형에 적합하지만 특별한 의미를 가지고 있다.
매직값의 일반적인 예는 값이 없거나 오류가 발생했음을 나타내기 위해 -1을 반환하는 것이다.

1. 매직값은 버그를 유발할 수 있다.

값이 없음을 나타내기 위해 함수에서 -1을 반환하는 것을 접한 적이 있을 것이다.
과거에는 더 명시적인 오류 전달 기법이나 널 또는 옵셔널 타입을 반환하는 것이 가능하지 않거나 실용적이지 않기 때문에 매직값을 반환하는 것이 어느 정보 합리적인 이유가 있었다. 레거시코드로 작업 중이거나, 신중하게 최적화해야 하는 코드가 있다면 여전히 이렇게 할 이유가 있을 수 있다. 그러나 일반적으로 매직값을 반환하면 예측을 벗어날 위험이 있으므로 사용하지 않는 것이 가장 바람직하다.

이것에 대한 해결책으로는
반환값으로 NULL을 주거나 옵셔널 또는 오류를 반환한다면
조금더 나은 코드를 사용할 수 있을 것이다.

2. 때때로 매직값이 우연히 발생할 수 있다.

매직값 반환이 반드시 개발자의 의도에 의해서만 되는 것은 아니다. 개발자가 자신의 코드에 주어지는 모든 입력과 이러한 입력값들이 어떤 영향을 미칠 수 있을지에 대해 충분히 생각하지 않을 때도 매직값은 반환될 수 있다.

널 객체 패턴을 적절히 사용하라

값을 얻을 수 없을 때 널값이나 빈 옵셔널을 반환하는 대신 널 객체 패턴을 사용할 수 있다.
널 객체 패턴을 사용하는 이유는 널값을 반환하는 대신 유효한 값이 반환되어 그 이후에 실행되는 로직에서 널값으로 인해 시스템에 피해가 가지 않도록 하기 위함이다.
이것의 가장 간단한 형태는 빈 문자열이나 빈 리스트를 반환하는 것이지만, 더 정교한 형태로는 모든 멤버 함수가 아무것도 하지 않거나 기본값을 반환하는 클래스를 구현하는 것이 있다.

1. 빈 컬렉션을 반환하면 코드가 개선될 수 있다.

함수가 리스트, 집합, 배열과 같은 컬렉션을 반환할 때 컬렉션의 값을 얻을 수 없는 경우가 있다.
값이 지정이 안됐다거나 주어진 상황에서 컬렉션에 값이 없을 수 있다.
이 경우 한 가지 방법은 널값을 반환하는 것이다.

2. 빈 문자열을 반환하는 것도 때로는 문제가 될 수 있다.

널 대신 빈 컬렉션을 반환함으로써 코드 품질을 향상시킬 수 있는 방법을 보여주었다. 일부 개발자들은 이것이 문자열에도 적용되어야 한다고 주장하는데, 이 경우 널 대신 빈 문자열이 반환되어야 한다는 것이다. 이 방식이 적절한지의 여부는 문자열이 어떻게 사용되는지에 달려있다. 어떤 경우에는 문자열이 문자들을 모아 놓은 것에 지나지 않으며, 이 경우 널 대신 빈 문자열을 반환하는 것이 적절할 수 있다. 문자열이 이것을 넘어서는 의미를 지닐ㄷ 때, 널 대신 빈 문자열을 반환하는 것이 문제가 될 수 있다.

  • 문자들의 모음으로서의 문자열은 빈 문자열 반환이 문제가 없다.
  • ID로서의 문자열의 경우 영향이 있을 수 있다. 그러므로 문자열이 없을 수 있음을 함수를 호출하는 쪽에서 명시적으로 인식하도록 하는 것이 중요하다.

3. 더 복잡한 널 객체는 예측을 벗어날 수 있다.

함수를 호출할 때 널 객체 패턴을 사용하는 것은 본질적으로 빈 상자를 파는 것과 같다.
호출하는 쪽에서 빈 상자를 받고 놀라거나 황당해할 가능성이 있다면, 널 객체 패턴을 피하는 것이 가장 좋을 것이다.

4. 널 객체 구현은 예상을 벗어나는 동작을 유발할 수 있다.

일부 개발자들은 널 객체 패턴에서 한 단계 더 나아가 널 객체 전용의 인터페이스나 클래스를 정의한다. 인터페이스나 클래스가 단순히 무언가를 반환하는 기능보다는 무언가를 수행하는 기능을 가지고 있을 때 이런 것이 필요한 것처럼 보일 수 있다.

널 객체 패턴은 여러 형태로 나타날 수 있다. 이것을 사용하거나 접할 때 그것이 정말 적절한지 아니면 예상을 벗어나는 동작을 할 가능성이 있는지에 대해 의식적으로 생각해봐야 한다. 널 안정성과 옵셔널을 사용하는 것이 인기를 얻음에 따라 '값이 없을'을 훨씬 쉽고 안전하게 나타낼 수 있게 되었다.
이와 함께 널 객체 패턴의 사용을 지지하는 기존의 주장들 중 많은 것들이 요즘에는 설득력이 떨어졌다.

예상치 못한 부수 효과를 피하라

부수 효과는 어떤 함수의 호출이 함수 외부에 초래한 상태 변화를 의미한다. 함수가 반환 하는 값 외에 다른 효과가 있다면 이는 부수 효과가 있는 것이다. 일반적인 부수 효과 유형은 다음과 같다.

  • 사용자에 출력 표시
  • 파일이나 데이터베이스에 무언가를 저장
  • 다른 시스템을 호출하여 네트워크 트래픽 발생
  • 캐시 업데이트 혹은 무효화

부수 효과는 소프트웨어 작성 시 불가피한 부분이다. 부수 효과가 없는 소프트웨어는 무의미할 것이다.
어느 시점에서 사용자, 데이터베이스, 다른 시스템 등에 무언가를 출력해야 한다. 이것은 적어도 코드의 일부에서는 부수 효과가 있어야 한다는 것을 의미한다.
부수 효과가 예상되고 코드를 호출한 쪽에서 그것을 원한다면 괜찮지만 부수 효과가 예상되지 않을 경우 놀라움을 유발하고 버그로 이어질 수 있다.

1. 분명하고 의도적인 부수 효과는 괜찮다.

호출하는 쪽에서 반드시 예상하거나 원하지 않는 부수 효과는 문제가 될 수 있다.

2. 예기치 않은 부수 효과는 문제가 될 수 있다.

예상치 못한 부수 효과가 문제의 소지가 될 수 있는 몇 가지 경우가 있다.

  • 부수 효과는 비용이 많이 들 수 있다.
    함수를 실행할 때 시간이 오래 걸린다거나 깜박임이 일어난다면, 대부분의 사용자는 그것을 끔찍한 버그로 해석할 수 있는 바람직하지 않은 기능이다.
  • 호출한 쪽의 가정을 깨트리기
    내가 작성한 코드를 다른 개발자는 부수효과를 파악하지 못했을 경우 버그를 일으킬 가능성이 있다.
  • 다중 스레드 코드의 버그

3. 부수 효과를 피하거나 그 사실을 분명하게 하라

함수를 작성할때 사용하려는 부수 효과가 정말 필요한지를 가장 먼저 질문해야 한다.
예측 가능한 코드를 작성하기 위한 가장 좋은 방법은 애초에 부수 효과를 일으키지 않는 것이다.
정말 부수 효과를 사용해야한다면 함수 명에 명시적으로 부수효과가 있다는걸 분명히 알 수 있도록 하는것이 좋다.

ex) 함수명 : getPixel() 부가 효과 : canvas.redraw() 변경되어야할 함수 명 : redrawAndGetPixel()

입력 매개변수를 수정하는 것에 주의하라

입력 매개변수를 수정하는 것은 예상을 벗어나는 코드 및 버그의 흔한 원인이 될 수 있다.

1. 입력 매개변수를 수정하면 버그를 초래할 수 있다.

입력으로 받은 객체에는 그 함수에 필요한 정보가 있지만, 이 함수가 호출된 뒤에도 해당 객체를 다른 용도로 사용할 가능성이 크다. 함수가 입력 매개변수를 수정하는 것은 페이지를 찢고 여백 위에 낙서하는 등의 일을 코드에서 하는 것이고 이것은 위험한 일이다.
호출하는 쪽에서는 일반적으로 객체를 빌려준다고 생각하고 함수에 전달한다. 이 함수가 자신의 코드에서 객체를 변경한다면 빌린 책을 손상하는 것과 같다.

2. 해결책: 변경하기 전에 복사하라

변경 전에 새 자료구조에 복사하는 것이 최상의 방법이다. 이렇게 한다면 원래의 객체가 변경되지 않는다.
값을 복사하면 메모리나 CPU, 혹은 두 가지 모두와 관련해 성능에 영향을 미칠 수 있다. 하지만 입력 매개변수의 변경으로 인해 발생할 수 있는 예기치 못한 동작이나 버그와 비교하면 성능이 크게 문제되지 않는 경우가 많다.

1개의 댓글

comment-user-thumbnail
2023년 6월 7일

"NULL"

답글 달기