SpringBoot Application을 80 port로 사용하고 싶은데, 리눅스 환경에서 80, 443과 같은 포트는 루트 권한으로 사용이 가능하다.
SpringBoot application의 내장 톰켓이 해당 포트를 사용할 수 있도록 sudo 권한을 사용해 서비스를 올리는 것 보다 proxy 서버를 올리는게 더욱 용이하므로 Nginx를 사용한 내용을 정리한다.

 

Nginx 란?

Apache의 1만명 규모 클라이언트의 동시 접속을 다루는 기술적인 이슈를 해결하기 위해 만든 Event-driven 구조의 오픈소스 서버 프로그램이다.
일반적인 HTTP 웹서버의 역할 외에도 proxy, reverse proxy를 제공한다.

 

설치 및 설정

Centos6

설치

$  yum install nginx

 

라우팅 설정

yum으로 nginx를 설치하게 되면 /etc/nginx 경로에 설치된다.
nginx 디렉토리 중 conf.d디렉토리 하위의 default.conf 파일을 변경하여 SpringBoot Application을 80 port로 포팅 한다.

$ vi /etc/nginx/conf.d/default.conf
# 아래와 같이 변경
# The default server
#

server {
#    listen       80 default_server; 
#    listen       [::]:80 default_server;
    listen       80;
    server_name  _;
    root         /usr/share/nginx/html;

    # Load configuration files for the default server block.
    include /etc/nginx/default.d/*.conf;

    location / {
#                SpringBoot application을 매핑
        proxy_pass http://localhost:9100;
    }

    error_page 404 /404.html;
        location = /40x.html {
    }

    error_page 500 502 503 504 /50x.html;
        location = /50x.html {
    }

}
server 블럭

도메인 단위의 1차 라우팅에 대한 설정을 담당한다..

  • listen : 서버 블록 정의 중 해당 서버에서 라우팅 할 특정 port를 정의하는 필드
    • default_server : 여러개의 server 블록을 작성할 때 default_server는 프로토콜 별로 단 하나의 server 블록에만 존재해야 합니다. 이 설정은 별도로 지정하지 않은 도메인으로 들어오는 다른 모든 요청에 대해서 해당 server 블록이 처리함을 의미한다.
  • server_name : 어떤 도메인을 라우팅 할지에 대한 필드

 

location 블럭

URI 리퀘스트의 path prefix의 매칭을 확인하여 적절한 어플리케이션으로 매핑한다.

  • proxy_pass : 실질적으로 요청 할 서비스를 명시하는 필드

 

Nginx 실행

아래의 명령어로 nginx 명령어 만으로 실행이 완료되고 별다른 알람 없이 실행이 완료되면 ps 명령어로 한번 더 확인 해 준다.

$ sudo nginx
$ ps -ef | grep nginx
root     20270     1  0 16:36 ?        00:00:00 nginx: master process nginx
nginx    20271 20270  0 16:36 ?        00:00:01 nginx: worker process
nginx    20272 20270  0 16:36 ?        00:00:01 nginx: worker process
nginx    20273 20270  0 16:36 ?        00:00:01 nginx: worker process
반응형

'Dev > Linux' 카테고리의 다른 글

[LINUX] JVM에서 thread dump 가져오기  (0) 2020.05.19
[Linux] 기본 명령어 정리(옵션) + 기타  (1) 2018.03.27

도메인

소프트웨어로 해결하고자 하는 문제영역을 도메인이라고 한다.
도메인은 하위 도메인으로 이루어 지기도 하는데, 예를 들어 주문이라는 도메인에는 혜택, 결제, 배송, 정산이라는 하위 도메인이 존재한다. 그러나 모든 도메인이 하위 도메인이 존재하는 것은 아니므로, 상황에 따라 하위 도메인 구성을 판단하면 된다.


도메인 모델

특정 도메인을 개념적으로 표현한 것을 도메인 모델이라고 한다.
도메인을 이해하기 위해서는 제공하는 기능과 도메인의 주요 데이터 구성을 파악해야 하는데, 이런 면에서 기능과 데이터를 함께 보여주는 객체 모델은 도메인을 모델링하기에 적합하다.
도메인 모델을 모델링 하는 방법은 객체 모델 외에도 상태 다이어그램이나 UML 표기기법 외에도 그래프(관계가 중요한 도메인일 시), 수학 공식(계산 규칙이 중요한 도메인일 시)을 이용해서 도메인을 모델링할 수 있다.
도메인 모델은 표현 방식이 중요한 것이 아니라 도메인을 잘 이해할 수 있도록 하는 개념 모델이다.
개념 모델은 바로 코드를 작성하기에 적합하지 않으므로 개념 모델을 최대한 따르는 구현 모델이 추가적으로 필요하다.


도메인 모델 패턴

일반적인 어플리케이션 아키텍처는 네 개의 계층으로 나뉜다.

  • 표현 - 사용자의 요청을 처리, 응답
  • 응용 - 직덥 도메인 로직을 가지지 않고 도메인 계층을 조합해서 사용자 요청에 대한 기능을 실행
  • 도메인 - 시스템이 제공할 도메인의 규칙을 구현
  • 인프라스트럭처 - DB와 같은 외부 시스템과 연동

도메인 모델 도출

개발자들은 기획서, 유즈케이스, 사용자 스토리 등을 통해 요구사항의 도메인을 이해한 후에 이를 통해 도메인 모델을 만들고 개발을 시작해야 한다.
도메인 모델링의 기본 작업?

  • 모델을 구성하는 핵심요소, 규칙, 기능을 찾기

엔티티와 밸류

도메인 모델은 두가지로 구분할 수 있음

Entity

  • 식별자를 가진다는 것이 큰 특징
  • 식별자는 바뀌지 않고 고유함
    • 엔티티를 구현한 클래스는 식별자를 이용해 equals(), hashCode()메서드를 구현할 수 있음

Entity 식별자 생성

Entity 식별자를 생성하는 시점은 도메인의 특징과 사용하는 기술에 따라 달라진다.

  • 특정 규칙에 따라 생성
  • UUID(Universally Unique Identifier) 사용
    • 다수의 개발 언어는 UUID 생성기를 제공
  • 값을 직접 입력
    • e.g.) 회원 아이디 / 이메일 : 중복 케이스 발생 가능성이 있으므로 중복 입력을 막는 로직이 필요
  • 일련번호 사용(시퀀스 / DB auto increment)

Value type

Value type은 개념적으로 완전한 하나(!??!?!!!)를 표현할 떄 사용

Bad :

public class ShippingInfo {
  // 받는 사람
  private String receiverName;
  private String receiverPhoneNumber;

  // 주소
  private String shippingAddress1; 
  private String shippingAddress2;
  private String shippingZipcode;

  ...
}

Good : 받는 사람, 주소 자체가 하나의 도메인 개념이므로 Value type을 사용함으로써 개념적으로 온전한 하나의 도메인을 표현

public class Receiver {
  private String name;
  private String phoneNumber;

  ...
}

public class Address {
  private String address1; 
  private String address2;
  private String zipcode;

  ...
}

public class ShippingInfo {
  private Receiver receiver;
  private Address address;

  ...
}

Value type의 장점

  • 해당 데이터(값/변수)의 도메인(개념/의미)을 명확하게 명시할 수 있음
  • 해당 Value type을 위한 기능 추가 가능

Value type 사용시 특징

  • 데이터 변경 시 해당 인스턴스의 값을 변경하는 것이 아닌 변경 할 값을 가지는 새로운 인스턴스를 생성하는 방식을 선호 -> 불변 객체로 사용하라!
    • 의도치 않은 곳에서 데이터가 변경되는 것을 방지하기 위함
  • Value 객체 비교 시, 모든 속성이 같은지 비교해야 함

엔티티 식별자와 밸류 타입

일반적으로 식별자는 String 타입이지만, 식별자가 도메인 특성을 가지는 경우 식별자를 위한 Value type을 사용해서 의미가 잘 드러나도록 한다.

도메인 모델에 set 메서드 넣지 않기

  • get/set 메서드로 인해(특히 set!) 값이 어디서든 바뀔 수 있기 때문에 도메인 핵심 개념이나 의도가 코드에서 사라지게 됨
  • 기본 생성자에 set 메서드로 값을 전달하다보면 객체 내부에 누락되는 데이터가 존재할 수 있음
    • 도메인 객체는 생성 시점에 필요한 것을 전달!

도메인 용어

도메인에서 사용하는 용어는 코드에 그대로 사용해야 가독성도 좋아지고 버그도 줄어들게 된다.

반응형

'Dev > Books' 카테고리의 다른 글

[CleanCode] 10장. 클래스  (0) 2020.05.31
[CleanCode] 9장. 단위 테스트  (0) 2020.05.18
[CleanCode] 8장. 경계  (0) 2020.05.18
[CleanCode] 7장. 예외 처리  (0) 2020.05.16
[CleanCode] 6장. 객체와 자료 구조  (0) 2020.05.15

목차

  1. TDD 법칙 세 가지
  2. 깨끗한 테스트 코드 유지하기
  3. 깨끗한 테스트 코드
  4. 테스트 당 assert 하나
  5. F.I.R.S.T

TDD 법칙 세 가지

  1. 실패하는 단위 테스트를 작성할 때까지 실제 코드를 작성하지 않는다.
  2. 컴파일은 실패하지 않으면서 실행이 실패하는 정도로만 단위 테스트를 작성한다.
  3. 현재 실패하는 테스트를 통과할 정도로만 실제 코드를 작성한다.

깨끗한 테스트 코드 유지하기

깨끗하지 않은 테스트 코드는 유지 보수에 많은 비용이 필요하게 되고, 테스트 코드가 없으면 프로덕션 코드의 결함율이 높아지게 된다.
테스트 코드는 유연성, 유지보수성, 재사용성을 제공해야 한다.


깨끗한 테스트 코드

깨끗한 테스트 코드는 명료성, 단순성, 풍부한 표현을 바탕으로 한 가독성이 필수이다.


테스트 당 assert 하나

assert 하나만을 사용하기 위해 아래와 같은 방법을 사용 할 수 있다.

  • 테스트를 쪼개서 assert를 분리
  • Template Method 패턴을 사용해 중복을 제거
  • @Before 함수에 given/when 부분을 분리

테스트 함수 하나는 개념 하나만 테스트 해야 한다!


F.I.R.S.T

  • Fast
  • Independent
  • Repeatable
    네트워크가 없는 환경에서도 실행 가능해야 함
  • Self-Validating
    테스트는 성공 혹은 실패로만 결과가 나와야 한다. 별도의 log 등을 통한 확인 작업은 절대 금물
  • Timely
    테스트 코드 작성 -> 실제 코드 작성


참고

BUILD-OPERATE-CHECK 패턴

Given-When-Then 패턴과도 비슷

  • Build (Given)
    Input 데이터를 생성
  • Operate (When)
    Build 단계에서 생성한 데이터로 실제 코드 실행
  • Check (Then)
    Operate 단계의 결과값을 확인

e.g.

public class BowlingTest {
  @Test
  public void FrameTest() {
    // Build (Given)
    Boll testBoll = Boll.getInstance(10);
    Frame frame = Frame.getInstance(Boll);
    // Operate (When)
       Boolean isStrike = frame.isStrike();
    // Check (Then)
    assertThat(isStrike).isTrue();
  }
}

TEMPLATE METHOD 패턴

어떠한 기능의 일부분을 서브 클래스로 캡슐화해 전체 일을 수행하는 구조는 바꾸지 않으면서 특정 단계의 수행 내역을 바꾸는 패턴
e.g.

Original :

public class SumoDeadLift {
  final void start() {
    stand();
    getBarbell();
    doOneRep();
  }

  private void stand() {  // 각 DeadLift 별로 수행 내역이 다른 부분
      System.out.println("다리를 넓게 벌리고 선다.");
  }

  private void getBarbell() {  // 모든 DeadLift의 공통된 알고리즘
    System.out.println("바벨을 들어올린다.");
  }

  private void doOneRep() {
    System.out.println("바벨을 다시 바닥으로 내려놓는다.");
  }
}

public class RumanianDeadLift {
  final void start() {
    stand();
    getBarbell();
    doOneRep();
  }

  private void stand() {
      System.out.println("다리를 골반 넓이 만큼 벌리고 선다.");
  }

  private void getBarbell() {
    System.out.println("바벨을 들어올린다.");
  }

  private void doOneRep() {
    System.out.println("정강이 중간까지만 내려간다");
  }
}

With Template Method Pattern: 모든 DeadLift의 수행 내역이 같은 부분은 추상 클래스에 정의한다.

public abstract class DeadLift {
  final void start() {
    stand();
    getBarbell();
    doOneRep();
  }

  abstract void stand();

  private void getBarbell() {  // 모든 DeadLift의 공통된 알고리즘
    System.out.println("바벨을 들어올린다.");
  }

  abstract void doOneRep();
}

public class SumoDeadLift extends DeadLift {
  @Override
  private void stand() {  // 각 DeadLift 별로 수행 내역이 다른 부분
      System.out.println("다리를 넓게 벌리고 선다.");
  }

  @Override
  private void doOneRep() {
    System.out.println("바벨을 다시 바닥으로 내려놓는다.");
  }
}

public class RumanianDeadLift extends DeadLift {
  @Override  
  private void stand() {
      System.out.println("다리를 골반 넓이 만큼 벌리고 선다.");
  }

  @Override
  private void doOneRep() {
    System.out.println("정강이 중간까지만 내려간다");
  }
}
반응형

'Dev > Books' 카테고리의 다른 글

[DDD Start!] 1장. 도메인 모델 시작  (0) 2020.06.06
[CleanCode] 10장. 클래스  (0) 2020.05.31
[CleanCode] 8장. 경계  (0) 2020.05.18
[CleanCode] 7장. 예외 처리  (0) 2020.05.16
[CleanCode] 6장. 객체와 자료 구조  (0) 2020.05.15

목차

  1. 오류 코드보다 예외를 사용하라
  2. Try-Catch-Finally 문부터 작성하라
  3. 미확인 예외를 사용하라
  4. 예외에 의미를 제공하라
  5. 호출자를 고려해 예외 클래스를 정의하라
  6. 정상 흐름을 정의하라
  7. null을 반환하지 마라
  8. null을 전달하지 마라

오류 코드보다 예외를 사용하라

오류 코드를 받아 처리 로직을 추가하는 것보다 오류가 발생하면 예외를 던지는게 좋음
Bad : 함수를 호출한 즉시 오류를 확인하지 않으면 문제가 발생할 확률이 높음

public class DeviceController {
  ...
  public void sendShutDown() {
    DeviceHandle handle = getHandle(DEV1);
    if (handle != DeviceHandle.INVALID) {
      retrieveDeviceRecord(handle);
      if (record.getStatus() != DEVICE_SUSPEND) {
        pauseDevice(handle);
        ...
      } else {
        logger.log("Device suspend.");
      }
    } else {
      logger.log("Invalid handle for: ");
    }
  }
}

Good :

public class DeviceController {
  ...
  public void sendShutDown() {
    try {
      tryToShutDown();
    } carch (DeviceShutDownError e) {
      logger.log(e);
    }
  }

  private void tryToShutDown() throws DeviceShutDownError {
    DeviceHandle handle = getHandle(DEV1);
    DeviceHandle record = retrieveDeviceRecord(handle);

    pauseDevice(handle);
    ...
  }

  private DeviceHandle getHandle(DeviceID id) {
    ...
       throw new DeviceShutDownError("Invalid handle for: ");
    ...
  }
}

Try-Catch-Finally 문부터 작성하라

try-catch-finally 문으로 시작하면 try 블록에서 무슨 일이 생기는지 호출자가 기대하는 상태를 정의하기 쉬워진다.


미확인 예외를 사용하라

확인된 예외

OCP(Open Closed Principle)를 위반하고 캡슐화가 꺠짐
예외를 던지는 메서드가 catch 블록이 있는 메서드가 아닌 더 하위에 있다면 그 사이의 메서드에서 모두 해당 예외를 선언부에 추가하거나 catch 블록에서 처리해야 함

public void get() {
  try {

  } catch (InvalidGetException e) {
    logger.log(e);
  }
}

public void getById() throws InvalidGetException {
  call();
}

public void call() throws InvalidGetException {
  throw new InvalidGetException();
}

예외에 의미를 제공하라

호출 스택만으로 사용자가 의도를 파악하기 어려우므로 오류 메세지에 정보를 담아 예외와 함께 던져야 한다.


호출자를 고려해 예외 클래스를 정의하라

프로그래머는 오류를 정의할 때 오류를 잡아내는 방법을 고려해야 한다.
외부 API의 다양한 예외를 직접 노출하지 않고 감싸기 기법을 통해 새로운 클래스를 만들어 캡슐화
Bad : 다른 종류의 예외를 처리 로직은 모두 같으므로 의미가 없음

ACMEPort port = new ACMEPort(12);

try {
  port.open();
} catch (DeviceResponseException e) {
  reportPortError(e);
  logger.log("Device response exception", e);
} catch (ATM1212UnlockedException e) {
  reportPortError(e);
  logger.log("Unlock exception", e);
} catch (GMXError e) {
  reportPortError(e);
  logger.log("Device response exception", e);
} finally {
  ...
}

Good :

public class LocalPort {
  private ACMEPort innerPort;

  public LocalPort(int portNumber) {
    innerPort = new ACMEPort(portNumber);
  }

  public void open() {
    try {
      innerPort.open();
    } catch (DeviceResponseException e) {
      throw new PortDeviceFailure(e);
    } catch (ATM1212UnlockedException e) {
      throw new PortDeviceFailure(e);
    } catch (GMXError e) {
      throw new PortDeviceFailure(e);
    }
  }
  ...
}

정상 흐름을 정의하라

외부 API를 감싸 독자적인 예외를 던져 중단한 뒤 호출하는 코드에서 처리를 정의해 중단된 계산을 처리하는 것은 대게 적합하지만 중단이 적합하지 않은 경우도 있다. 이러한 경우 특수 사례 패턴(SPECIAL CASE PATTERN)을 적용해 개선한다.
특수 사례 패턴이란?
반환할 값이 없을 때 예외를 던지는 것이 아니라 기본값을 반환

Bad :

try {
  MealExpenses expenses = expenseReportDAO.getMeals(employee.getID());
  m_total += expenses.getTotal();
} catch (MealExpensesNotFound e) {
  m_total += getMealPerDien();
}

Good :

public int testMethod() {
  ...
    MealExpenses expenses = expenseReportDAO.getMeals(employee.getId());
    m_total += expenses.getTotal();
  ...
}

public class PerDiemMealExpenses implements MealExpenses {
  private static final int MEAL_EXPENSES_DEFAULT = 3000;
  public int getTotal() {
        ...
    // 반환 값이 없을 경우 기본값으로 일일 기본 식비를 반환한다.
    return MEAL_EXPENSES_DEFAULT;
  }
}

null을 반환하지 마라

메서드가 null을 반환하면 해당 메서드를 사용하는 클라이언트는 null 체크 코드를 추가할 수 밖에 없다. 이 과정에서 null 체크가 누락되면 버그로 이어지고, null 체크 코드는 아름답지 못하다. 특수 사례 객체를 통해 이러한 문제를 해결할 수 있다.
Bad :

List<Employee> employees = getEmployees();
if (eployees != null) {
  for (Employee e : employees) {
    totalPay += e.getPay();
  }
}

Good :

List<Employee> employees = getEmployees();
for (Employee e : employees) {
  totalPay += e.getPay();
}

public List<Employee> getEmployees() {
  if (.. 직원이 없다면 ..) {
    return Collections.emptyList();
  }
}

null을 전환하지 마라

메서드의 argument로 null을 전달하게 되면 메서드는 내부에서 null 체크 또는 assert를 이용하여 처리하는 로직을 추가할 수 밖에 없다. 이러한 로직을 통해 exception을 던지는 메서드가 되면 해당 메서드를 사용하는 쪽에서도 exception 처리를 추가해야 한다.
호출자가 넘기는 null을 처리하는 방법은 없다. 정책적으로 null을 넘기지 못하게 하는것이 가장 합리적이다.

반응형

'Dev > Books' 카테고리의 다른 글

[CleanCode] 9장. 단위 테스트  (0) 2020.05.18
[CleanCode] 8장. 경계  (0) 2020.05.18
[CleanCode] 6장. 객체와 자료 구조  (0) 2020.05.15
[CleanCode] 5장. 형식 맞추기  (0) 2020.05.12
[CleanCode] 4장. 주석  (0) 2020.05.12

목차

  1. 자료 추상화
  2. 자료/객체 비대칭
  3. 디미터 법칙
  4. 자료 전달 객체

자료 추상화

변수를 함수를 통해 계층을 추가한다고 해서 구현이 저절로 감춰지지는 않는다.
추상 인터페이스를 제공해 사용자가 구현을 모르는 채 자료의 핵심을 조작할 수 있어야 클래스라고 할 수 있다.
Bad :

// 해당 클래스에 getX, getY 메서드를 추가한다 해도 사용자는 x, y를 반환할 뿐일 것이라고 추측이 가능하다  
// 이것은 전혀 추상화되지 않은 상태이다
public class Point {
  public double x;
  public double y;
}

Good :

// x, y가 실제 직교 좌표계인지, 극좌표계인지 알 수 없다  
// 내부에서 변수가 바뀌던, 로직이 바뀌던 클라이언트는 상관이 없다
public interface Point {
  double getX();
  double getY();
  void setCartesian(double x, double y);
  double getR();
  double getTheta();
  void setPolar(double r, double theta);
}

자료/객체 비대칭

객체 vs 자료 구조

  • 객체
    추상화 뒤로 자료를 숨긴 채 자료를 다루는 함수만 공개
  • 자료 구조
    자료를 그대로 공개
    별도의 함수는 제공하지 않음

객체 지향 코드 vs 절차 지향 코드

  • 객체 지향 코드
    기존 함수를 변경하지 않으면서 새 클래스를 추가하기 용이
    새로운 함수를 추가하기가 어려움
  • 절차 지향 코드
    기존 자료 구조를 변경하지 않으면서 새 함수를 추가하기 용이
    새로운 자료구조를 추가하기 어려움

필요에 따라 자료구조 + 절차 지향 코드가 객체 지향 코드보다 나을 수 있다. 필요에 따라 사용하자.


디미터 법칙

모듈은 자신이 조작하는 객체의 속사정을 몰라야 한다는 법칙
한 객체의 메서드가 반환하는 객체의 메서드를 호출하면 안됨

  • 기차 충돌
    Bad : 기차 충돌 상황

    final String outputDir = ctxt.getOptions().getScratchDir().getAbsolutePath();
    
    String outFile = outputdir + "/" + className.replace('.', '/') + ".class";
    FileOutputStream fout = new FileOutputStream();
    BufferedOutputStream bos = new BufferedOutputStream(fout);

    Better :

    Options opts = ctxt.getOptions();
    File scratchDir = opts.getScratchDir();
    final String outputDir = scratchDir.getAbsolutePath();

    기차 충돌 상황보다는 낫지만 각 메서드가 반환하는 값이 객체인지 자료 구조인지에 따라 디미터 법칙 위반여부가 결정됨
    객체라면 내부 구조를 getter를 통해 노출시키는 것이므로 디미터 법칙을 위반하는 것
    getter가 객체인지 자료 구조인지 알 수 없게 혼동을 주므로 아래와 같이 명확하게 한다.

    Good :

    final String outputDir = ctxt.options.scratchDir.absolutePath;
  • 잡종 구조
    공개 변수가 있으면서 공개 조희/설정 함수가 있고 기능을 수행하는 함수도 있는 구조

  • 구조체 감추기
    기차 충돌 예시에서 ctxt, opts, scratchDir이 진짜 객체라면?
    내부의 객체를 가져다 쓰는 것이 아닌 메세지를 보내 필요한 정보를 반환 받도록 해야 한다.

    // outputDir의 목적이 무엇인지를 확인하여 수정한다.
    BufferedOutputStream bos = ctxt.createScratchFileStream(classFileName);

자료 전달 객체

Data Transfer Object(DTO) - 공개 변수만 있고 함수가 없는 클래스
데이터베이스와 통신하거나 소켓에서 받은 메세지의 구문을 분석할 때 유용

  • bean 구조
    private 변수와 생성자, gettter만 존재
  • 활성 레코드
    DTO의 일반적인 형태에 save, find와 같은 탐색 함수도 제공
    데이터베이스 테이블이나 다른 소스에서 자료를 직접 변환한 결과
    비즈니스 규칙 메서드를 직접 추가하지 않도록 조심해야 함
반응형

'Dev > Books' 카테고리의 다른 글

[CleanCode] 8장. 경계  (0) 2020.05.18
[CleanCode] 7장. 예외 처리  (0) 2020.05.16
[CleanCode] 5장. 형식 맞추기  (0) 2020.05.12
[CleanCode] 4장. 주석  (0) 2020.05.12
[CleanCode] 3장. 함수  (0) 2020.05.06

목차

  1. 형식을 맞추는 목적
  2. 적절한 행 길이를 유지하라
  3. 가로 형식 맞추기
  4. 팀 규칙

프로그래머는 규칙에 맞게 형식을 깔끔하게 맞춰 코드를 짜야 한다.
팀으로 일한다면 팀이 합의해 규칙을 정하고 모두가 그 규칙을 따라야 한다.


형식을 맞추는 목적

맨 처음 잡아놓은 코드의 스타일과 가독성은 유지보수 용이성과 확정성에 계속 영향을 미친다.


적절한 행 길이를 유지하라

  • 신문 기사처럼 작성하라
    소스 파일은 고차원 개념/알고리즘 -> 저차원 함수와 세부 내역 순으로 아래로 내려갈수록 의도를 세세하게 표현
  • 개념은 빈 행으로 분리하라
  • 세로 밀집도
    서로 밀접한 코드 행은 세로로 가까이 놓여야 함
  • 수직 거리
    변수 선언 - 사용하는 위치에 최대한 가까이 선언
    인스턴스 변수 - 클래스의 맨 처음에 선언, 변수 간에 세로로 거리를 두지 않음
    종속 함수 - 한 함수에서 호출하는 다른 함수는 세로로 가까이 배치 (함수 -> 호출되는 함수 순서)
    개념적 유사성 - 친화도가 높을수록 코드를 가까이 배치 (한 함수가 다른 함수를 호출하는 직접적인 종속성, 변수와 그 변수를 사용하는 함수, 명명법이 똑같거나 기능이 같은 함수)
  • 세로 순서
    함수 호출 종속성은 아래 방향으로 유지, 자연스럽게 소스 코드 모듈이 고차원 -> 저차원으로 기술

가로 형식 맞추기

Max 120자

  • 가로 공백과 밀집도
    공백을 통해 개념을 분리 (e.g., 할당 연산자, parameter 분리를 위한 ", ")
    연산자 우선순위 구분을 위한 공백 추가
  • 가로 정렬
    정렬이 필요할 정도로 목록이 길다면 클래스를 쪼개는 방법을 고려
  • 들여쓰기
    scope로 이뤄진 계층을 표현하기 위해 사용
    1줄의 if/while/함수도 꼭 {} 괄호를 사용하며 들여쓰기를 사용
  • 가짜 범위
    빈 while/for문은 다음 행에 세미콜론을 들여써서 작성

팀 규칙

팀에 속해 있다면?
각자의 선호하는 규칙 < 팀 규칙

반응형

'Dev > Books' 카테고리의 다른 글

[CleanCode] 7장. 예외 처리  (0) 2020.05.16
[CleanCode] 6장. 객체와 자료 구조  (0) 2020.05.15
[CleanCode] 4장. 주석  (0) 2020.05.12
[CleanCode] 3장. 함수  (0) 2020.05.06
[CleanCode] 2장. 의미있는 이름  (0) 2020.05.02

+ Recent posts