반응형

1. 서론

이번 포스팅에서는 성능 & 안전을 고려한 Code에 대해 진행하려고 합니다.

 

Clean Code에서 말하는 성능 & 안전측면에서의 코드는 아래와 같습니다.

 

  • Synchronized 부분은 가능한 작게.
  • null 반환 X, null 전달 X
  • 멀티 쓰레드 코드 시 유의

2. Synchronized 부분은 가능한 작게.

 

Java 에서의 Synchronized는 멀티 쓰레드 환경에서 데이터의 동시접근을 막아 데이터 정합을 지키기 위해 사용합니다.

 

방법은 Synchronized를 정의한 객체를 lock을 걸어 동시 접근을 막는것입니다.

 

객체에 lock을 걸게되어 성능은 당연히 좋지 않게됩니다.

 

그렇기 때문에, 최대한 Synchronized를 피하는 방식으로 개발을 하는것이 best입니다.

 

하지만, 부득이하게 Synchronized를 사용해야 하는경우에는 가능한 작게 만들어

성능에 큰 이슈가 나지 않도록 해야합니다.

 

3. null 반환 X, 메서드에 전달 X 

 

null을 사용한다는 것은 매우 위험하고 깨끗하지 않은 코드입니다.

 

그 이유는 아래와 같습니다.

 

  • NullPointerException 발생 여지를 만들게 됩니다.
  • null 체크를 위해 코드가 난잡하게 됩니다.

Clean Code에서는 위와 같은 이유로 특수목적의 객체 혹은 빈 객체를 반환하는것을 추천합니다.

 

또한, Java 사용자의 경우에는 8 버전의 Optional을 통하여 간편하게  Null 체크 및 예외를 핸들링 할 수 있습니다.

 

 

 

 

 

 

반응형

 

 

 

 

 

 

 

4. 멀티 쓰레드 코드 시 유의

 

단일 쓰레드가 아닌 멀티 쓰레드의 경우에는 고려할 점이 많습니다.

위에서 언급한 Synchronized, dead lock 등이 대표적입니다.

 

그렇기 때문에 clean code에서는 아래와 같이 유의하라고 나옵니다.

 

  • 문제를 발생시킬 만한 테스트를 작성하고 자주 수행하라.
  • 시스템 오작동을 one-off("한번만 일어나는")로 판단해 무시하지 말라.
  • 여러 환경에서 실행할 수 있게 하라.
  • 스레드 관련 버그는 단일 스레드에서 먼저 잘 동작하는지 확인 후 멀티 스레도 코드로 작성하라.
  • 프로세서 수보다 많은 스레드를 돌려 작업 전환이 잦게하여 critical section이나 dead lock을 찾을 확률을 높여라.

 

위 유의사항들을 한 마디로 요약하면, Error를 무시하지 말고 끝없이 테스트를 진행하라 입니다.

 

멀티쓰레드 환경에서는 수많은 케이스들이 존재하며 에러를 재현하기도 힘듭니다.

 

그렇기 때문에, 저의 경우에는 로컬에서는 단위 테스트 & 잡 테스트를 진행하며

개발망에서는 실제 배포하여 최소 3일정도의 결과를 모니터링 후 리얼에 배포를 진행하고 있습니다.

 

5. 마무리

 

성능 & 안전을 고려한 Code에 대해서 포스팅하였습니다.

다음에는 테스트 Code에 대해 포스팅하겠습니다.

반응형

'Programming > CleanCode' 카테고리의 다른 글

(3) Clean Code - 테스트 Code  (0) 2020.03.01
(1) Clean Code - readability가 좋은 Code  (0) 2020.02.27
반응형

1. 서론

이번 포스팅에서는 Clean Code에 대해 공부한 내용을 공유하고자 합니다.

책을 읽으며 제가 느낀 Clean Code는 아래와 같습니다.

 

  • readability가 좋은 Code
  • 성능 & 안전을 고려한 Code
  • 테스트 Code

이번 포스팅에서는 readability가 좋은 Code 부터 포스팅하도록 하겠습니다.

 

2. readability가 좋은 Code

 

readability 는 코드의 성능부분에서 잘짜는것과는 별개입니다.

 

readability를 좋은 코드의 이점은 아래와 같습니다.

 

  1. 반복이 가능한 Code 생성
  2. 중복이 없는 Code 생성
  3. 불필요한 Code 제거

Clean Code 책의 readability 관련부분은 아래와 같습니다.

 

1) 클래스, 메소드, 변수에 대해서 의도있는 이름 사용

 

naming만 잘되어 있어도 어떠한 책임을 가지고 있는 클래스인지 어떠한 일을 수행하는 메소드인지 알게됩니다.

이것은, 남의 코드를 분석 혹은 재빨리 파악해야할 경우 매우 유용하며 잘짠 코드라고 할 수 있습니다.

아래와 같은 예를 들 수 있습니다.

boolean validationResult = validateProduct(product);

위에는 상품의 validation을 수행하는 코드입니다.

validationProduct 메소드에서 무슨일이 일어나는지 보지 않아도,

상품을 받아 validation 수행 후 boolean 타입의 결과값을 주는것을 알 수 있습니다.

 

2) 소문자 L, 대문자 O의 사용 자제

 

소문자 L은 숫자 1과 비슷해 보이며, 대문자 O는 숫자 0과 비슷해보입니다.

이런경우 code convention에 맞지 않더라도 대문자 L, 소문자 o을 쓰시는게 더욱 가독성이 좋습니다.

아래와 같은 예를 들 수 있습니다.

long count = 123456L;

long형의 값 뒤에 소문자 l이 아닌 대문자 L을 붙이는것이 더욱 가독성이 좋은것을 볼 수 있습니다.

 

3) 클래스 - 명사구, 메소드 - 동사구 사용

 

클래스는 행위를 할 수 있는 물체 혹은 사람의 개념을 담아냅니다.

그렇기 때문에 동사구보다는 명사구를 사용하여 명확한 의미를 전달하여야 합니다.

아래와 같은 예를 들 수 있습니다.

Customer, Seller - O
Attack, Send - x

반대로, 메소드는 행위를 의미하기 때문에 동사구를 사용합니다.

customer, seller - X
attack, send - O

 

4) 함수는 작게, if while 문은 한줄로!!

 

개발을 하다보면 한 코드가 방대해질때가 있습니다.

이 경우, 메소드로 분리하여 사용해야 합니다.

예로, 이미지를 다운받아 validation을 거쳐 업로드하는 일련의 과정이 있습니다.

위 일련의 과정이 한 메소드에 있다면, 매우 보기 힘든 코드일 것입니다.

하지만, 아래와 같이 메소드를 나누어 호출하는 형태로 변경하면 어떨까요?

  • download
  • validate
  • upload

일련의 과정이 메소드명만으로도 명확하게 무슨일을 하는지 볼 수 있습니다.

if, while 문과 같은 조건문도 동일합니다.

아래와 같이 if문에서 메소드를 호출하여 boolean을 반환받는다면 깔끔한 코드가 될 것입니다.

if( validate(image) )

 

5) 코드는 위에서 아래로 규칙을 가지게 해야한다.

 

메소드안에서 메소드를 호출하는 경우가 있습니다.

이런경우, 의미적으로 상하 관계를 갖기 때문에 코드 또한 위에서 아래로 하는것이 좋습니다.

아래는 한 예입니다.

public void a() {
    boolean result = b();
    return result;
}

private boolean b() {
    return true;
}

 

 

 

 

 

반응형

 

 

 

 

 

 

6) 함수인수로는 max가 2개!!

 

함수인수는 많으면 많을수록 가독성이 떨어지게 됩니다.

java의 경우 같은 데이터 타입이 있다면 첫번째, 두번째 인수의 의미도 알기 어렵습니다.

이를 100% 막을수는 없습니다.

단, 이러한 함수들을 무분별하게 만드는것을 최소화하기 위해 함수인수로는 max 2개라는 내용이 있습니다.

 

7) 출력인수는 배제

 

출력인수는 함수의 반환 값이 아닌 입력 인수로 결과를 받는 경우입니다.

아래와 같은 예가 될수 있습니다.

appendFooter(report)

아래는 report라는 객체에 값을 추가하는 메소드입니다.

위는 report에 추가하는지 report에 있는 내용을 추가하는지 명시적이지 않습니다.

이런 경우, 아래와 같은 방법으로 바꿀수 있습니다.

report.appendFooter()

report에 footer를 추가하는게 명시적으로 보입니다.

또한, 최근 DDD에 맞는 도메인 메소드 사용으로 봐도 무관합니다.

 

8) 오류 코드보다는 try-catch

 

이 부분은 오류를 회피하기 위해 if-else문으로 늘어진 형태의 코드에서

try-catch 블록으로 코드를 짜는게 더욱 명시적인것을 의미합니다.

이 경우, Exception을 custom하여 더욱 명시적인 Exception을 만들수 도 있습니다.

아래와 같은 Exception이 예시입니다.

FileSizeInvalidException

해당 예외를 내뱉으면 개발자는 바로 '아 파일 사이즈가 유효하지 않구나!' 라고 알 것입니다.

 

9) 한 함수에는 최대한 return이 하나만!!

한 함수에 한개의 출구만을 담으라는 내용입니다.

만약, 한 함수내에서 return이 여러번 일어나는 케이스가 있다면?

함수를 작게 쪼개면 가능합니다!!!

작게 쪼갠 함수들의 조합으로 하나의 return을 만드는 것이죠!!

아래는 한 예입니다.

public boolean validateProduct(Product product) {
    return validateProductName(product.getName()) && validateProductImage(product.getImage())
}

개발하다보면 위의 경우보단, return을 2~3개 쓰는게 더욱 명시적일때가 있습니다.

이런경우에는 과감히 return을 쓰시는게 맞다고 생각합니다.

Clean Code의 본의미는 코드가 간결하며, 유지보수에 좋은 이쁜코드를 짜기 위한 내용이기 때문입니다.
또한, 개발에는 정해진것이 없기 때문에 현재 제가 포스팅하는 Clean Code 책의 내용도 참고로만 보시면 좋습니다.

 

10) 주석

 

Clean Code 책에서는 아래와 같은 경우 주석을 다는것을 추천하고 있습니다.

 

  • 정보를 제공하는 주석
  • 의도를 설명하는 주석
  • 결과를 경고하는 주석
  • TODO
  • 중요성을 강조하는 주석

 

11) 개념은 빈행으로 구분

 

코드의 개념이 다르다고 생각하는 부분은 빈행을 추가하여 코드의 가독성을 높이는게 좋습니다.

 

아래의 예를 볼 수 있습니다.

private String resultVal = "result";
public String a() { 
	return resultVal;
}


private String resultVal = "result";

public String a() { 
	return "A";
}

 

12) 밀접한 코드는 세로로 밀집

 

위 11번과는 반대로 개념이 다른것은 빈행으로 구분하며, 개념상 밀접한것은 세로로 붙여 사용해야 한다는 의미입니다.

 

아래의 예를 들수 있습니다.

 

// good
private String ProductId;
private String ProductName;


// bad
private String ProductId;

private String ProductName;

 

 

 

13) 함수 변수는 사용하는 곳에 가장 가까운거리에 선언

 

클래스 인스턴스 변수의 경우에는 가장 최 상위에 배치합니다.

 

하지만, 특정 함수내에만 사용하는 변수는 함수의 가장 가까운 곳에 배치합니다.

 

이것 역시, 클래스 인스턴스 변수와 혼동을 방지하고 가독성을 증가키게 됩니다.

 

14) 클래스 인스턴스 변수간 세로로 거리를 두지 않는다

 

클래스 인스턴스 변수의 경우 세로로 거리를 두게되면 인스턴스 변수인지 아닌지 혼동이 오게됩니다.

 

이런것을 방지하고자, 세로로 빈행을 두지 않는게 관례적입니다.

 

 

15) 객체는 동작만을 제공하고 자료는 숨겨야한다

 

이것은 모두 알고 계신 캡슐화의 기본 내용입니다.

 

자신의 속성값은 숨기며, 메소드만을 통하여 상호 객체간 데이터를 처리한다는 의미입니다.

 

하지만, 최근 setter 역시 데이터를 변경가능하게 열어주는 동작으로 사용을 자제하고 있습니다.

 

 

16) 클래스의 변수 메소드 순서

 

자바의 code convention에 따르면 메소드 순서는 아래와 같습니다.

 

  1. static public
  2. static private
  3. public 인스턴스 
  4. private 인스턴스 

 

17) 클래스의 큰 메소드 -> 작은 메소드 -> 클래스 분리

 

처음 클래스, 메소드 설계가 어렵다면 먼저 기능우선으로 개발을 시작하자.

저의 경우에도 일단, 개발을 시작 후 기능 테스트가 통과된다면 코드 리팩토링에 들어갑니다.

 

위와 같이 개발을 하게되면, 클래스 내에 큰 메소드가 생성될 케이스가 있습니다.

 

이 경우, 리팩토링 시 메소드를 분리합니다. 분리를 하고 끝나면 안됩니다.

분리 후 개념이 분리되는지도 클래스를 전체적으로 살핍니다.

 

개념이 분리된다면 클래스를 나누어 각 클래스에 맞는 메소드로 분리합니다.

 

저는 예로 신규 상품, 변경 상품, 품절 상품에 대해 처리하는 메소드들이 한 Service 클래스에 있는것을

코드 리팩토링과 동시에 신규, 변경, 품절 클래스로 분리 후 메소드 분리를 동시에 진행하였습니다.

 

추가로 각 클래스를 핸들러 메소드 패턴을 통하여 가독성을 높이는 작업을 진행한 경험이 있습니다.

 

3. 마무리

이번에는 readability가 좋은 Code에 대해서 포스팅하였습니다.

다음에는 성능 & 안전을 고려한 Code에 대해 포스팅하겠습니다.

 

반응형

'Programming > CleanCode' 카테고리의 다른 글

(3) Clean Code - 테스트 Code  (0) 2020.03.01
(2) Clean Code - 성능 & 안전을 고려한 Code  (0) 2020.02.29
반응형

1. 서론

카프카 사용시 고려사항에 대해 포스팅을 진행하겠습니다.

실제로, 카프카를 운영 및 사용하게 된다면 고려할 사항들은 생각보다 많습니다.

그 중, 3가지 정도만을 추려 공유드리고자 합니다.

공유 드릴 내용은 아래와 같습니다.

  1. idempotent (멱등성) 보장
  2. produce 역전 가능성
  3. 카프카 성능

2. idempotent (멱등성) 보장

카프카는 pub-sub 구조입니다.

이 말은, sub 하는 입장에서 동일한 메시지를 한번이 아닌 여러번 처리할 수 있다는 얘기가 됩니다.

결국, consumer를 개발할 시 idempotent (=멱등성)이 보장되어야 합니다.

idempotent란 같은 input에 대해서는 항상 동일한 결과가 나오는 것을 의미합니다.

idempotent가 보장되지 않는다면 데이터가 꼬여버리는 현상을 맞이하게 될 것입니다.

 

 

 

 

 

 

반응형

 

 

 

 

 

 

3. produce 역전 가능성

이전 포스팅에서 partition이 1인 경우 메시지의 순서가 보장된다고 한적이 있습니다.

정확하게 말씀드리면 보장이 되지 않습니다.

그 이유는 아래 그림을 보며 설명해 드리도록 하겠습니다.

카프카는 내부적으로 producer에게 받은 메시지를 buffer에 저장 후, 차례대로 broker 서버의 disk에 write 하게 됩니다.

하지만, 이 buffer에 역전되어 들어가는 상황이라면

위 그림의 2번 메시지가 먼저 write하게 되며 아래그림처럼 저장이 됩니다.

 

2번 메시지가 1번 메시지보다 offset이 작아지게 되고 결국, 개발할 경우 항상 역전에 대해 고려를 해야합니다.

 

저의 경험을 공유드리자면, 저는 메시지를 sub하여 DB CUD를 치는 상황이었습니다.

이슈는 아래와 같은 방법으로 해결하였습니다.

 

  1. 메시지에 timestamp 값 추가.
  2. 메시지를 pollSize 만큼 가져와 sorting 혹은 merge 수행.
  3. DB 테이블에 version 칼럼을 추가하여 timestamp 값 기준으로 optimistic lock CUD 수행.

DB에 칼럼까지 추가한 이유는 poll 과 poll 사이에 역전이 된 메시지가 존재할 수 있기 때문입니다.

 

4. 카프카 성능

 

카프카는 메시지 유입 시, leader에 write 후 follower가 fetch write를 하게됩니다.

 

이때, fetch write시 카프카는 os에서 제공하는 shared page cache를 사용합니다.

 

shared page cache에는 최근 메시지를 저장하게 되고, follower는 이 cache 에 메시지가 없는경우 disk 접근하여 fetch write를 수행합니다.

 

또한, 이 shared page cache는 리눅스의 cgroup의 memory limit에 영향을 받습니다.

 

그러므로, 카프카의 메시지 복제 성능을 향상하고 싶은 경우 cgroup의 memory limit을 늘려주는것도 하나의 방법입니다.

 

5. 마무리

이번 포스팅에서는 실제로 제가 겪은 카프카 사용시 고려사항들에 대해 포스팅 하였습니다.

 

이렇게, 카프카 관련 1~5의 포스팅을 완료 하였습니다.

 

감사합니다.

반응형

'MQ > Kafka' 카테고리의 다른 글

(7) Kafka Trouble Shooting  (0) 2021.11.19
(6) spring kafka + schema registry + gradle plugin 적용  (4) 2020.10.22
(4) 카프카 매니저 & 스키마 레지스트리  (0) 2020.02.26
(3) 카프카 사용 예제  (0) 2020.02.25
(2) 카프카 설치  (0) 2020.02.25

+ Recent posts