다짐하자/출근 준비

22.01.24 - [ 개발상식 ] Development common sense

giggs 2023. 1. 24. 21:28

 

 

-- INDEX --

 

 

1. 좋은 코드란
무엇인가
2. Object Oriented
Programming
(객체 지향
프로그래밍)
3. RESTful API 4. TDD
(Test-Driven
Development)
5. 함수형
프로그래밍
읽기 쉬운 코드 인간 중심적
프로그래밍
패러다임
설계 중심에 자원,
HTTP Method
를 통해 처리
Add a test immutable vs mutable
중복이 없는
코드
(추출, 추상화)
코드의
재사용성에
따른
생산성
, 디버깅, 
유지보수의 이점
REST
6
가지 특징
Run all tests and see
if new one fails
first-class citizen
일관성 있는
코드
예측 불가
상태에 대한
버그
RESTful 하게 
API
디자인한다?
Refactor code Reactive Programming
  객체 지향적
설계 원칙 
( SOLID )
어떠한 장점 / 단점이 있는가 의문점들  

 

 

 


 

 

 

 

1. 좋은 코드란 무엇인가

  • 모두가 ‘좋은 코드’의 기준이 조금씩 다르고 각각의 경험을 기반으로 좋은 코드를 정의하고 있다.
  • 세간에 좋은 코드의 정의는 정말 많다.
  • 읽기 쉬운 코드
  • 중복이 없는 코드
  • 일관성 있는 코드 등등

 


 

1-1 : 읽기 쉬운 코드

  • 주석을 달아서 코드를 이해시키고 읽기 좋게 해 주기? 주석은 관리하기 어렵다.
  • 주석은 기본적으로 메타데이터이기 때문에 주석의 내용과 함수의 실제 동작이 일치한다고 보장할 수 없다. 
  • 수시로 변하는 요구사항을 반영하느라 함수가 수정될 때 주석이 함께 수정되지 않을 수도 있다.
  • 모든 개발자가 동일한 배경지식을 가진 것은 아니다.
  • 읽기 쉽다는 것은 단순히 코드를 읽는 것만 의미하는 것이 아니라 이해하는 것까지 포함하기 때문에 나무뿐만 아니라 숲을 볼 때도 쉬워야 한다.
  • 읽기 쉬운 코드는 테스트 코드 작성을 용이하게 할 수 있다.

 

 


 

2-2 : 중복이 없는 코드

  • 개발자라면 누구나 A와 B에서 동일한 로직을 수행하는 코드를 보는 순간, 추출(extraction)하고 싶어 한다.
  • 동일한 로직을 수행하는 코드가 여기에도 저기에도 있는 것은 굉장히 불편하기 때문이다.
  • 오래전에 작성해 둔 코드에서 A라는 코드 조각이 두 군데에 퍼져있다는 것을 기억하기 쉽지 않다. 때문에 한 곳의 A만 수정되었을 때 다른 한 곳에서 버그가 발생한다. 이러한 경험에 기반하여 동일한 로직을 수행하는 코드는 별도의 함수로 빼두고 재사용하려고 한다.
  • 그땐 같았지만 지금은 같지 않은 그때는 중복이었던 코드 조각을 필요에 의해 추출했지만, 지금은 아니라서 다시 합치거나 수정하고 있다. 보통 하는 일은 동일하지만, 그 목적이 다른 코드를 추출했을 때, 많이 발생하는데 이것은 변경 사항을 반영하는 데 비용이 들어가게 한다.
  • 쓰이지 않는 코드
  • 쓰지 않게 된 코드(Dead code)가 있는데, 이 코드가 어디에서 쓰일지도 모른다는 불안감이 있다면 감히 삭제하지 못할 것이고 이 코드는 깨진 유리창 중 하나가 된다.
  • 한 파일에 여러 로직이 얽혀있을 때 각 코드 조각 중 서로 의존 관계에 있는 것들을 추출해야 한다.
  • 이렇게 추상화가 된 함수는 하나의 목적(역할)을 갖게 되고 의미 있는 추출(추상화)이 이루어진다.

 

추출(extraction)

‘추출하다(extract)‘의 어원을 살펴보면 ex(밖으로)와 tract(끌어내기)의 합성어이다. 특별한 기준 없이 단순히 밖으로 끌어내는 것을 의미한다.

 


추상화(abstraction)

‘추상화하다(abstract)‘의 어원은 조금 다르다. ab(먼 개념)와 tract(이끌어내기)의 합성어로, 어떤 대상의 중요한 요점들을 재해석하여 정리한 것이라고 해석할 수 있다.

 

 


 

1-3 : 일관성 있는 코드 

  • 최소한의 가독성을 보장하는 방법은 일관성 있는 코드를 작성하는 것이다. 일관성은 합의된 규칙을 기반으로 만들어지며 이 합의된 규칙은 개개인에게 동일하게 다가간다.
  • 코드에 일관성이 지켜진다면 예측이 가능하다. 예측이 가능하다는 것은 어느 곳에 어떤 코드가 위치하는지 예상할 수 있다는 것이다. 프로젝트 팀원 간의 그라운드 룰(Ground Rule)이 필요한 이유이다.
  • Naming 규칙 - 변수명 네이밍도 중요하지만 작성되는 수많은 함수의 네이밍에도 규칙이 있으면 일관성을 지킬 수 있다.
  • Directory 규칙 - ‘어디에 분리할지’ - 일관된 디렉터리 구조는 전체적인 구조를 파악하는 데 큰 도움이 되고 컴포넌트 간의 관계를 파악하는 데에도 도움을 준다.

 

 


 

 

 

2. Object Oriented Programming ( 객체 지향 프로그래밍 )

 

2-1 : 인간 중심적 프로그래밍 패러다임

  • 객체 지향 프로그래밍 이전의 프로그래밍 패러다임을 살펴보면, 중심이 컴퓨터에 있었다.
  • 컴퓨터가 사고하는대로 프로그래밍을 하는 것이다.
  • 하지만 객체지향 프로그래밍이란 인간 중심적 프로그래밍 패러다임이라고 할 수 있다.
  • 즉, 현실 세계를 프로그래밍으로 옮겨와 프로그래밍하는 것을 말한다.
  • 현실 세계의 사물들을 객체라고 보고 그 객체로부터 개발하고자 하는 애플리케이션에 필요한 특징들을 뽑아와 프로그래밍하는 것이다. 이것을 추상화라 한다.

 

 


 

2-2 : 코드의 재사용성에 따른 생산성, 디버깅, 유지보수의 이점

  • OOP 로 코드를 작성하면 이미 작성한 코드에 대한 재사용성이 높다.
  • 자주 사용되는 로직을 라이브러리로 만들어두면 계속해서 사용할 수 있으며 그 신뢰성을 확보할 수 있다.
  • 또한 라이브러리를 각종 예외상황에 맞게 잘 만들어두면 개발자가 사소한 실수를 하더라도 그 에러를 컴파일 단계에서 잡아낼 수 있으므로 버그 발생이 줄어든다.
  • 또한 내부적으로 어떻게 동작하는지 몰라도 개발자는 라이브러리가 제공하는 기능들을 사용할 수 있기 때문에 생산성이 높아지게 된다.
  • 객체 단위로 코드가 나눠져 작성되기 때문에 디버깅이 쉽고 유지보수에 용이하다.
  • 또한 데이터 모델링을 할 때 객체와 매핑하는 것이 수월하기 때문에 요구사항을 보다 명확하게 파악하여 프로그래밍할 수 있다.

 


 

2-3 : 예측 불가 상태에 대한 버그

  • 객체 간의 정보 교환이 모두 메시지 교환을 통해 일어나므로 실행 시스템에 많은 overhead 가 발생하게 된다.
  • 하지만 이것은 하드웨어의 발전으로 많은 부분 보완되었다.
  • 객체 지향 프로그래밍의 치명적인 단점은 함수형 프로그래밍 패러다임의 등장 배경을 통해서 알 수 있다.
  • 바로 객체가 상태를 갖는다는 것이다.
  • 변수가 존재하고 이 변수를 통해 객체가 예측할 수 없는 상태를 갖게 되어 애플리케이션 내부에서 버그를 발생시킨다는 것이다.
  • 이러한 이유로 함수형 패러다임이 주목받고 있다.

 

 


 

2-4 : 객체 지향적 설계 원칙 ( SOLID )

  1. SRP(Single Responsibility Principle) : 단일 책임 원칙
    클래스는 단 하나의 책임을 가져야 하며 클래스를 변경하는 이유는 단 하나의 이유이어야 한다.
  2. OCP(Open-Closed Principle) : 개방-폐쇄 원칙
    확장에는 열려 있어야 하고 변경에는 닫혀 있어야 한다.
  3. LSP(Liskov Substitution Principle) : 리스코프 치환 원칙
    상위 타입의 객체를 하위 타입의 객체로 치환해도 상위 타입을 사용하는 프로그램은 정상적으로 동작해야 한다.
  4. ISP(Interface Segregation Principle) : 인터페이스 분리 원칙
    인터페이스는 그 인터페이스를 사용하는 클라이언트를 기준으로 분리해야 한다.
  5. DIP(Dependency Inversion Principle) : 의존 역전 원칙
    고수준 모듈은 저수준 모듈의 구현에 의존해서는 안된다.

 

 


 

 

 

3. RESTful API

 

 

3-1 : API 설계 중심에 자원이 있고, HTTP Method 를 통해 이 자원을 처리하도록 설계하는 것

  • 위키백과의 정의 요약 : 월드 와이드 웹(World Wide Web a.k.a WWW)과 같은 분산 하이퍼미디어 시스템을 위한 소프트웨어 아키텍처의 한 형식으로 자원을 정의하고 자원에 대한 주소를 지정하는 방법 전반에 대한 패턴
  • REST란, REpresentational State Transfer의 약자이다.
  • 여기에 ~ful 이라는 형용사형 어미를 붙여 ~한 API 라는 표현으로 사용된다.
  • 즉, REST 의 기본 원칙을 성실히 지킨 서비스 디자인은 'RESTful'하다라고 표현할 수 있다.
  • REST가 디자인 패턴이다, 아키텍처다 많은 이야기가 존재하는데, 하나의 아키텍처로 볼 수 있다. 좀 더 정확한 표현으로 말하자면, REST 는 Resource Oriented Architecture 이다.
  • API 설계의 중심에 자원(Resource)이 있고 HTTP Method 를 통해 자원을 처리하도록 설계하는 것이다.

 

 


 

3-2 : REST 6가지 특징

 

uniform Interface

  • - URI로 지정한 리소스에 대한 조작을 통일되고 한정적인 인터페이스로 수행하는 아키텍처 스타일
  • 전체적인 시스템 아키텍처를 간단하고 잘 파악할 수 있도록 하기 위한 약속된 Interface

 


Stateless - 무상태성

  • - 상태정보(세션, 쿠키)를 따로 저장하고 관리하지 않는다.
  • 클라이언트에서 서버로의 각 요청에는 그 요청을 이해하는 데 필요한 모든 정보가 포함되어야 합니다.
  • 서버에 저장된 환경 정보를 이용해서 이득[서버에서의 클라이언트 정보 유지 등]을 취하면 안 됩니다. 따라서 세션의 정보는 전적으로 클라이언트가 가지고 있어야 합니다.
  • 로그인했다는 세션 유지가 필요하다면 그 정보 또한 Client가 해당 정보를 가지고 서버에 전달해야 한다

 


Cacheable - 캐시 가능

  • - HTTP라는 기존 웹표준을 그대로 사용하기 때문에, 웹에서 사용하는 기존 인프라를 그대로 활용이 가능합니다. 따라서 HTTP가 가진 캐싱 기능이 적용 가능합니다. HTTP 프로토콜 표준에서 사용하는 Last-Modified태그나 E-Tag를 이용하면 캐싱 구현이 가능합니다.
  • 요청에 대한 응답 내의 데이터에 해당 요청은 캐시가 가능한지 불가능 한지 명시해야 합니다. 응답을 캐시 할 수 있다면 클라이언트에서 동일한 요청이 왔을 때 응답 데이터를 재사용할 수 있어야 합니다.
  • cache-control 헤더를 통하여 캐시 여부 명시

Client-Server 구조 

  • REST 서버는 API 제공, 클라이언트는 사용자 인증이나 컨텍스트(세션, 로그인 정보)등을 직접 관리하는 구조로 각각의 역할이 확실히 구분되기 때문에 클라이언트와 서버에서 개발해야 할 내용이 명확해지고 서로 간 의존성이 줄어들게 됩니다.
  • 클라이언트와 서버가 서로 의존하지 않고 별도로 진화할 수 있습니다. 클라이언트는 서버의 리소스 URI만 알고 있으면 되기 때문입니다.

Hierarchical system - 계층형 구조 

  • REST 서버는 다중 계층으로 구성될 수 있으며 보안, 로드 밸런싱, 암호화 계층을 추가해 구조상의 유연성을 둘 수 있고 PROXY, 게이트웨이 같은 네트워크 기반의 중간매체를 사용할 수 있게 합니다.
  • 계층화된 시스템 아키텍처를 사용하여 각 구성들 간의 계층을 마음대로 상호작용 할 수 없도록 제한 함으로 써 Interface를 일원화할 수 있습니다.

Code on demand -

  • 서버가 네트워크를 통해 클라이언트에 전달한 javascript 등과 같은 프로그램들은 그 자체로 실행이 될 수 있어야 한다. 이것은 사전 구현에 필요한 기능의 수를 줄임으로써 클라이언트를 단순화합니다.
  • 이 말은 우리가 평소에는 정적인 데이터를 xml 또는 json에 담아서 client로 보내고 client가 이것을 가공합니다. 하지만 code on demand라는 것은 client에 보내는 데이터를 바로 실행 가능한 코드를 보내서 이것을 Client에서 실행하는 것을 말합니다.

 

 


 

 

3-3 : RESTful 하게 API 를 디자인한다는 것은 무엇을 의미하는가.(요약)

  • 리소스와 행위를 명시적이고 직관적으로 분리한다.
  • 리소스는 URI로 표현되는데 리소스가 가리키는 것은 명사로 표현되어야 한다.
  • 행위는 HTTP Method로 표현하고, GET(조회), POST(생성), PUT(기존 entity 전체 수정), PATCH(기존 entity 일부 수정), DELETE(삭제)을 분명한 목적으로 사용한다.
  • Message 는 Header 와 Body 를 명확하게 분리해서 사용한다.
  • Entity 에 대한 내용은 body 에 담는다.
  • 애플리케이션 서버가 행동할 판단의 근거가 되는 컨트롤 정보인 API 버전 정보, 응답받고자 하는 MIME 타입 등은 header 에 담는다.
  • header 와 body 는 http header 와 http body 로 나눌 수도 있고, http body 에 들어가는 json 구조로 분리할 수도 있다.
  • API 버전을 관리한다.
  • 환경은 항상 변하기 때문에 API 의 signature 가 변경될 수도 있음에 유의하자.
  • 특정 API 를 변경할 때는 반드시 하위호환성을 보장해야 한다.
  • 서버와 클라이언트가 같은 방식을 사용해서 요청하도록 한다.
  • 브라우저는 form-data 형식의 submit 으로 보내고 서버에서는 json 형태로 보내는 식의 분리보다는 json 으로 보내든, 둘 다 form-data 형식으로 보내든 하나로 통일한다.
  • 다른 말로 표현하자면 URI 가 플랫폼 중립적이어야 한다.

 

 


 

3-4 : 어떠한 장점이 존재하는가?

  1. Open API 를 제공하기 쉽다
  2. 멀티플랫폼 지원 및 연동이 용이하다.
  3. 원하는 타입으로 데이터를 주고받을 수 있다.
  4. 기존 웹 인프라(HTTP)를 그대로 사용할 수 있다.

 


 

3-5 : 단점은 뭐가 있을까?

  1. 사용할 수 있는 메소드가 한정적이다.
  2. 분산환경에는 부적합하다.
  3. HTTP 통신 모델에 대해서만 지원한다.

 

 


 

 

 

4. TDD ( Test-Driven Development )

  • Test-Driven Development(TDD)는 매우 짧은 개발 사이클의 반복에 의존하는 소프트웨어 개발 프로세스이다.
  • 우선 개발자는 요구되는 새로운 기능에 대한 자동화된 테스트케이스를 작성하고 해당 테스트를 통과하는 가장 간단한 코드를 작성한다.
  • 일단 테스트 통과하는 코드를 작성하고 상황에 맞게 리팩토링하는 과정을 거치는 것이다.
  • 말 그대로 테스트가 코드 작성을 주도하는 개발방식인 것이다.

 

 

 

4-1 : Add a test

  • 테스트 주도형 개발에선, 새로운 기능을 추가하기 전 테스트를 먼저 작성한다.
  • 테스트를 작성하기 위해서, 개발자는 해당 기능의 요구사항과 명세를 분명히 이해하고 있어야 한다.
  • 이는 사용자 케이스와 사용자 스토리 등으로 이해할 수 있으며, 이는 개발자가 코드를 작성하기 전에 보다 요구사항에 집중할 수 있도록 도와준다.
  • 이는 정말 중요한 부분이자 테스트 주도 개발이 주는 이점이라고 볼 수 있다.

 

 


 

4-2 : Run all tests and see if new one fails

  • 어떤 새로운 기능을 추가하면 잘 작동하던 기능이 제대로 작동하지 않는 경우가 발생할 수 있다.
  • 더 위험한 경우는 개발자가 이를 미처 인지하지 못하는 경우이다.
  • 이러한 경우를 방지하기 위해 테스트 코드를 작성하는 것이다.
  • 새로운 기능을 추가할 때 테스트 코드를 작성함으로써, 새로운 기능이 제대로 작동함과 동시에 기존의 기능들이 잘 작동하는지 테스트를 통해 확인할 수 있는 것이다.

 

 


 

4-3 : Refactor code

  • '좋은 코드'를 작성하기란 정말 쉽지가 않다. 코드를 작성할 때 고려해야 할 요소가 한두 가지가 아니기 때문이다.
  • 가독성이 좋게 coding convention 을 맞춰야 하며, 네이밍 규칙을 적용하여 메소드명, 변수명, 클래스명에 일관성을 줘야 하며, 앞으로의 확장성 또한 고려해야 한다.
  • 이와 동시에 비즈니스 로직에 대한 고려도 반드시 필요하며, 예외처리 부분 역시 빠뜨릴 수 없다. 물론 코드량이 적을 때는 이런저런 것들을 모두 신경 쓰면서 코드를 작성할 수 있지만 끊임없이 발견되는 버그들을 디버깅하는 과정에서 코드가 더럽혀지기 마련이다.
  • 이러한 이유로 코드량이 방대해지면서 리팩토링을 하게 된다.
  • 이때 테스트 주도 개발을 통해 개발을 해왔다면, 테스트 코드가 그 중심을 잡아줄 수 있다.
  • 뚱뚱해진 함수를 여러 함수로 나누는 과정에서 해당 기능이 오작동을 일으킬 수 있지만 간단히 테스트를 돌려봄으로써 이에 대한 안심을 하고 계속해서 리팩토링을 진행할 수 있다.
  • 결과적으로 리팩토링 속도도 빨라지고 코드의 퀄리티도 그만큼 향상하게 되는 것이다.
  • 코드 퀄리티 부분을 조금 상세히 들어가 보면, 보다 객체지향적이고 확장 가능이 용이한 코드, 재설계의 시간을 단축시킬 수 있는 코드, 디버깅 시간이 단축되는 코드가 TDD 와 함께 탄생하는 것이다.
  • 어차피 코드를 작성하고 나서 제대로 작동하는지 판단해야 하는 시점이 온다.
  • 물론 중간중간 수동으로 확인도 할 것이다. 또 테스트에 대한 부분에 대한 문서도 만들어야 한다.
  • 그 부분을 자동으로 해주면서, 코드 작성에 도움을 주는 것이 TDD 인 것이다. 끊임없이 TDD 찬양에 대한 말만 했다. TDD를 처음 들어보는 사람은 이 좋은 것을 왜 안 하는가에 대한 의문이 들 수도 있다.

 

 


 

4-4 : 의문점들

 

Q. 코드 생산성에 문제가 있지는 않나?

  • 두 배는 아니더라도 분명 코드량이 늘어난다.
  • 비즈니스 로직, 각종 코드 디자인에도 시간이 많이 소요되는데, 거기에다가 테스트 코드까지 작성하기란 여간 벅찬 일이 아닐 것이다.
  • 코드 퀄리티보다는 빠른 생산성이 요구되는 시점에서 TDD는 큰 걸림돌이 될 수 있다.

 


Q. 테스트 코드를 작성하기가 쉬운가?

  • 이 또한 TDD라는 개발 방식을 적용하기에 큰 걸림돌이 된다. 진입 장벽이 존재한다는 것이다.
  • 어떠한 부분을 테스트해야 할지, 어떻게 테스트해야 할지, 여러 테스트 프레임워크 중 어떤 것이 우리의 서비스와 맞는지 등 여러 부분들에 대한 학습이 필요하고 익숙해지는 데에도 시간이 걸린다.
  • 팀에서 한 명만 익숙해진다고 해결될 일이 아니다.
  • 개발은 팀 단위로 수행되기 때문에 팀원 전체의 동의가 필요하고 팀원 전체가 익숙해져야 비로소 테스트 코드가 빛을 발하게 되는 것이다.

 


Q. 모든 상황에 대해서 테스트 코드를 작성할 수 있는가? 작성해야 하는가?

  • 세상에는 다양한 사용자가 존재하며, 생각지도 못한 예외 케이스가 존재할 수 있다.
  • 만약 테스트를 반드시 해봐야 하는 부분에 있어서 테스트 코드를 작성하는데 어려움이 발생한다면? 이러한 상황에서 주객이 전도하는 상황이 발생할 수 있다.
  • 분명 실제 코드가 더 중심이 되어야 하는데 테스트를 위해서 코드의 구조를 바꿔야 하나 하는 고민이 생긴다.
  • 또한 발생할 수 있는 상황에 대한 테스트 코드를 작성하기 위해 배보다 배꼽이 더 커지는 경우가 허다하다.
  • 실제 구현 코드보다 방대해진 코드를 관리하는 것도 쉽지만은 않은 일이 된 것이다.
  • 모든 코드에 대해서 테스트 코드를 작성할 수 없으며 작성할 필요도 없다.
  • 또한 테스트 코드를 작성한다고 해서 버그가 발생하지 않는 것도 아니다.
  • 애초에 TDD는 100% coverage와 100% 무결성을 주장하지 않았다.

 

 

 


 

 

 

 

5. 함수형 프로그래밍

  • 함수형 프로그래밍의 가장 큰 특징 두 가지는 immutable data와 first class citizen으로서의 function이다.

 

 

5-1 : immutable vs mutable

  • 우선 immutable과 mutable의 차이에 대해서 이해를 하고 있어야 한다. 
  • immutable이란 말 그대로 변경 불가능함을 의미한다. 
  • immutable 객체는 객체가 가지고 있는 값을 변경할 수 없는 객체를 의미하여 값이 변경될 경우, 새로운 객체를 생성하고 변경된 값을 주입하여 반환해야 한다.
  • 이와는 달리, mutable 객체는 해당 객체의 값이 변경될 경우 값을 변경한다.

 

 


 

5-2 : first-class citizen

  • 함수형 프로그래밍 패러다임을 따르고 있는 언어에서의 함수(function)는 일급 객체(first class citizen)로 간주된다.
  • 일급 객체라 함은 다음과 같다.
  • 변수나 데이터 구조안에 함수를 담을 수 있어서 함수의 파라미터로 전달할 수 있고, 함수의 반환값으로 사용할 수 있다.
  • 할당에 사용된 이름과 관계없이 고유한 구별이 가능하다.
  • 함수를 리터럴로 바로 정의할 수 있다.

 

 


 

5-3 : Reactive Programming

  • 반응형 프로그래밍(Reactive Programming)은 선언형 프로그래밍(declarative programming)이라고도 불리며, 명령형 프로그래밍(imperative programming)의 반대말이다.
  • 또 함수형 프로그래밍 패러다임을 활용하는 것을 말한다.
  • 반응형 프로그래밍은 기본적으로 모든 것을 스트림(stream)으로 본다.
  • 스트림이란 값들의 집합으로 볼 수 있으며 제공되는 함수형 메소드를 통해 데이터를 immutable 하게 관리할 수 있다.

 

 

 


 

 

# 참고자료

 

https://github.com/JaeYeopHan/Interview_Question_for_Beginner/tree/master/Development_common_sense

 

GitHub - JaeYeopHan/Interview_Question_for_Beginner: Technical-Interview guidelines written for those who started studying progr

:boy: :girl: Technical-Interview guidelines written for those who started studying programming. I wish you all the best. :space_invader: - GitHub - JaeYeopHan/Interview_Question_for_Beginner: Techn...

github.com