CS : 개발 상식

개발 상식

CS 공부를 하면서 정리해둔 것입니다. 핵심 키워드는 Bold 해놓겠습니다!

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

OOP는 현실 세계의 사물들을 객체라고보고 그 객체의 관점에서 프로그래밍 하는 것을 의미합니다.

특징

  • 캡슐화(Encapsulation) : 객체의 속성과 기능을 하나로 묶어 실제 구현 내용을 외부에 감추어 은닉하는 방법
  • 상속(Inheritance) : 상위 클래스의 모든걸 하위 클래스가 모두 이어 받는 것입니다.
  • 다형성(Polymorphism) : 형태가 같지만 다른 기능을 하는 것을 말합니다. (오버로딩이나 오버라이딩을 이용해 구현할 수 있습니다.)
  • 추상화(abstraction) : 공통된 특징을 파악해 정의해 놓은 설계 기법입니다.

장점

  • 재사용성이 높습니다.
  • 라이브러리를 각종 예외상황에 맞게 잘 만들어두면 실수를 하더라도 에러를 컴파일 단계에서 잡아낼 수 있으므로 버그 발생이 줄어듭니다.
  • 다양한 라이브러리를 사용해 생산성을 높일 수 있습니다.
  • 객체 단위로 코드가 나누어져 작성되기 때문에 유지보수에 용이합니다.
  • 데이터 모델링을 할 때 객체와 매핑하는 것이 수월하기 때문에 요구사항을 보다 명확하게 파악하여 프로그래밍할 수 있습니다.

단점

  • 객체대한 이해가 필요하기 때문에 개발속도가 느립니다.
  • 실행속도가 느립니다.
  • 다중 상속이 지원되는 C++ 같은 경우에 너무 복잡해져 코딩난이도가 상승합니다.

객체 지향적 설계 원칙 (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) : 의존 역전 원칙

    고수준 모듈저수준 모듈구현에 의존해서는 안됩니다.

함수형 프로그래밍 (functional programming)

y = f(x) 형태의 함수로 구성된 프로그래밍 기법입니다. 데이터를 파라미터로 전달하고 결과를 받는 코드로 구성되어 있습니다.

First-citizen

함수형 프로그래밍 패러다임을 따르고 있는 언어에서의 함수(function)일급 객체(first class citizen)로 간주됩니다. 일급 객체라 함은 다음과 같습니다.

  • 변수나 데이터 구조안에 함수를 담을 수 있어서 함수의 파라미터로 전달할 수 있고, 함수의 반환값으로 사용할 수 있습니다.
  • 할당에 사용된 이름과 관계없이 고유한 구별이 가능합니다.
  • 함수를 리터럴로 바로 정의할 수 있습니다.

객체지향 프로그래밍보다 효율적인 경우

  • 대용량 데이터 처리에 유리합니다.
    • 데이터를 포장해서 객체를 생성하는 것 보다데이터를 바로 처리하는 것이 속도에 유리합니다.
    • 멀티코어 CPU에서 데이터를 병렬 처리하고 취합할 때 객체보다는 함수가 유리함.
  • 이벤트 지향 프로그래밍
    • 반복적인 이벤트 처리핸들러 객체보다는 핸들러 함수가 적합함.

현대 프로그래밍 기법은 객체지향 프로그래밍 + 함수형 프로그래밍으로 구성되어 있습니다.

Reference

함수형 프로그래밍 사용법

RESTful API

REST란, REpresentational State Transfer 의 약자입니다. 여기에 ~ful 이라는 형용사형 어미를 붙여 ~한 API 라는 표현으로 사용됩니다. 즉, REST 의 기본 원칙을 성실히 지킨 서비스 디자인은 RESTful하다라고 표현할 수 있습니다.

API 설계의 중심에 자원(Resource)이 있고 HTTP Method 를 통해 자원을 처리하도록 설계하는 것입니다.

특징

  1. Uniform (유니폼 인터페이스)

    ​ Uniform Interface는 URI로 지정한 리소스에 대한 조작통일되고 한정적인 인터페이스로 수행하는 아키텍처 스타일을 말합니다.

  2. Stateless (무상태성)

    ​ REST는 무상태성 성격을 갖습니다. 다시 말해 작업을 위한 상태정보를 따로 저장하고 관리하지 않습니다. 세션 정보나 쿠키정보를 별도로 저장하고 관리하지 않기 때문에 API 서버는 들어오는 요청만을 단순히 처리하면 됩니다. 때문에 서비스의 자유도가 높아지고 서버에서 불필요한 정보를 관리하지 않음으로써 구현이 단순해집니다.

  3. Cacheable (캐시 가능)

    ​ REST의 가장 큰 특징 중 하나는 HTTP라는 기존 웹표준을 그대로 사용하기 때문에, 웹에서 사용하는 기존 인프라를 그대로 활용이 가능합니다. 따라서 HTTP가 가진 캐싱 기능이 적용 가능합니다. HTTP 프로토콜 표준에서 사용하는 Last-Modified태그나 E-Tag를 이용하면 캐싱 구현이 가능합니다.

  4. Self-descriptiveness (자체 표현 구조)

    ​ REST의 또 다른 큰 특징 중 하나는 REST API 메시지만 보고도 이를 쉽게 이해 할 수 있는 자체 표현 구조로 되어 있다는 것입니다.

  5. Client - Server 구조

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

  6. 계층형 구조

    ​ REST 서버는 다중 계층으로 구성될 수 있으며 보안, 로드 밸런싱, 암호화 계층을 추가해 구조상의 유연성을 둘 수 있고 PROXY, 게이트웨이 같은 네트워크 기반의 중간매체를 사용할 수 있게 합니다.

RESTful 하게 API 를 디자인 한다는 것은 무엇을 의미하는가

  1. 리소스행위 를 명시적이고 직관적으로 분리합니다.

    • 리소스는 URI로 표현되는데 리소스가 가리키는 것은 명사로 표현되어야 합니다.

    • 행위는 HTTP Method로 표현하고, GET(조회), POST(생성), PUT(기존 entity 전체 수정), PATCH(기존 entity 일부 수정), DELETE(삭제)을 분명한 목적으로 사용합니다.

  2. MessageHeader 와 Body 를 명확하게 분리해서 사용합니다.

    • Entity 에 대한 내용은 body 에 담습니다.

    • 애플리케이션 서버가 행동할 판단의 근거가 되는 컨트롤 정보인 API 버전 정보, 응답받고자 하는 MIME 타입 등은 header 에 담습니다.

    • header 와 body 는 http header 와 http body 로 나눌 수도 있고, http body 에 들어가는 json 구조로 분리할 수도 있습니다.

  3. API 버전을 관리합니다.

    • 환경은 항상 변하기 때문에 API 의 signature 가 변경될 수도 있음에 유의합니다.

    • 특정 API 를 변경할 때는 반드시 하위호환성을 보장해야 합니다.

  4. 서버와 클라이언트같은 방식을 사용해서 요청하도록 합니다.

    • 브라우저는 form-data 형식의 submit 으로 보내고 서버에서는 json 형태로 보내는 식의 분리보다는 json 으로 보내든, 둘 다 form-data 형식으로 보내든 하나로 통일합니다.

    • 다른 말로 표현하자면 URI 가 플랫폼 중립적이어야 합니다.

장점

  • Open API 를 제공하기 쉽습니다

  • 멀티플랫폼 지원 및 연동이 용이합니다.

  • 원하는 타입으로 데이터를 주고 받을 수 있습니다.

  • 기존 웹 인프라(HTTP)를 그대로 사용할 수 있습니다.

단점

  • 사용할 수 있는 메소드가 4 가지 밖에 없습니다.

  • 분산환경에는 부적합합니다.

  • HTTP 통신 모델에 대해서만 지원합니다.

TDD(Test-Driven Developmen)

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

Add a test

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

Run all tests and see if new one fails

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

Refactor code

‘좋은 코드’를 작성하기란 정말 쉽지가 않습니다. 코드를 작성할 때 고려해야 할 요소가 한 두 가지가 아니기 때문입니다. 가독성이 좋게 coding convention 을 맞춰야 하며, 네이밍 규칙을 적용하여 메소드명, 변수명, 클래스명에 일관성을 줘야하며, 앞으로의 확장성 또한 고려해야 합니다. 이와 동시에 비즈니스 로직에 대한 고려도 반드시 필요하며, 예외처리 부분 역시 빠뜨릴 수 없습니다. 물론 코드량이 적을 때는 이런 저런 것들을 모두 신경쓰면서 코드를 작성할 수 있지만 끊임없이 발견되는 버그들을 디버깅하는 과정에서 코드가 더럽혀지기 마련입니다.

이러한 이유로 코드량이 방대해지면서 리팩토링을 하게 됩니다. 이 때 테스트 주도 개발을 통해 개발을 해왔다면, 테스트 코드가 그 중심을 잡아줄 수 있습니다. 뚱뚱해진 함수를 여러 함수로 나누는 과정에서 해당 기능이 오작동을 일으킬 수 있지만 간단히 테스트를 돌려봄으로써 이에 대한 안심을 하고 계속해서 리팩토링을 진행할 수 있습니다. 결과적으로 리팩토링 속도도 빨라지고 코드의 퀄리티도 그만큼 향상하게 되는 것입니다. 코드 퀄리티 부분을 조금 상세히 들어가보면, 보다 객체지향적이고 확장 가능이 용이한 코드, 재설계의 시간을 단축시킬 수 있는 코드, 디버깅 시간이 단축되는 코드가 TDD 와 함께 탄생하는 것입니다.

의문점들

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

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

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

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

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

세상에는 다양한 사용자가 존재하며, 생각지도 못한 예외 케이스가 존재할 수 있습니다. 만약 테스트를 반드시 해봐야 하는 부분에 있어서 테스트 코드를 작성하는데 어려움이 발생한다면? 이러한 상황에서 주객이 전도하는 상황이 발생할 수 있습니다. 분명 실제 코드가 더 중심이 되어야 하는데 테스트를 위해서 코드의 구조를 바꿔야 하나하는 고민이 생깁니다. 또한 발생할 수 있는 상황에 대한 테스트 코드를 작성하기 위해 배보다 배꼽이 더 커지는 경우가 허다합니다. 실제 구현 코드보다 방대해진 코드를 관리하는 것도 쉽지만은 않은 일이 된 것입니다.

모든 코드에 대해서 테스트 코드를 작성할 수 없으며 작성할 필요도 없습니다. 또한 테스트 코드를 작성한다고 해서 버그가 발생하지 않는 것도 아닙니다.


참고 : https://github.com/JaeYeopHan/Interview_Question_for_Beginner/tree/master/Development_common_sense#part-1-1-development-common-sense

댓글남기기