인프런 "모든 개발자의 실무를 위한 필수 기본기 클래스" 강의를 듣고 정리한 내용 + 따로 검색하여 공부한 내용입니다.
프로그래밍 패러다임
프로그래밍을 어떤 기준으로 바라보고 작성할 것인지에 대한 관점을 프로그래밍 패러다임이라고 한다.
함수적 호출 과정(프로시저)을 중심으로 설계한다면 절차 지향 프로그래밍이 되고, 객체 중심으로 설계한다면 객체 지향 프로그래밍이 된다.
가장 대중적인 프로그래밍 패러다임은 객체 지향 프로그래밍이다.
대표적으로 절차 지향 언어로 C언어가 있으며, 객체 지향 언어로는 자바가 있다.
파이썬과 자바스크립트는 이 두 패러다임을 모두 수용하는 멀티 패러다임 언어이다.
절차 지향 프로그래밍
순차적인 함수 호출 중심의 관점을 갖는다.
메인 로직부터 하위 로직까지 Top -> Down 식으로 구성되어 이해하기 쉽다.
객체지향과 가장 큰 차이점은 데이터를 중앙 집중식으로 관리한다는 점이다. 즉, 프로세스 로직과 데이터가 별도로 분리되어 있다.
프로세스 로직 부분에서는 어떤 데이터가 들어오는지 모르기 때문에 로직 내 조건문이 많은 경향이 있다.
장점으로는, 코드를 이해하기 쉽고 직관적이며 복잡한 로직이 아니라면 유지 보수하기가 쉽다는 점이 있다.
단점으로는, 코드를 확정하거나 자주 실행에 따라 로직이 변경되는 경우 수정하기가 어렵고,
데이터가 중앙 집중식으로 함수와 분리되어 있어 함수가 많아지면 데이터의 변경 사항을 추적하기 어려워진다는 점이 있다.
따라서 절차지향 프로그래밍은 프로그램 수행하는 알고리즘이 명확하고, 기능 확장 등이 자주 일어나지 않는 상황에서 용이하다.
객체지향 프로그래밍
객체들의 책임과 협력 중심의 관점을 갖는다.
객체라고 하는 단위에 책임을 명확히 하고 서로 협력하도록 프로그래밍을 하는 패러다임이다.
데이터와 함수가 분리된 절차 지향과 달리 객체 안에 데이터와 함수가 하나로 되어 객체가 하나의 역할과 수행을 책임지게 된다.
객체 내부의 데이터는 외부에 공개할 필요가 없거나 해서는 안 되는 데이터라면 모두 자신 내부에 숨긴다.
코드가 더 복잡해 지지만, 기능을 확장할 때 효과적이다. 상속을 이용하면 한 번 정의해 놓은 메서드를 파생 클래스에서 재사용 가능하다.
같은 역할을 하는 객체를 쉽게 바꾸도록 설계할 수 있다. (다형성)
장점으로는, 다형성과 의존성 주입으로 코드를 확장하기 쉬우며, 실행 환경의 다양한 입력에 대응하기 좋다는 점이 있다.
또한, 여러 명의 개발자들이 협력해야 하거나, 확장 가능하도록 코드를 설계해야 하는 경우에 적합하다.
단점으로는, 런타임이 되기 전에 실제로 코드가 어떤 방향으로 흐르는지 알기 어려우며, 디버깅도 어렵다.
확장이 가능하고 유연한 만큼, 처음에는 어렵고 헷갈린다는 점이 있다.
함수형 프로그래밍
상태를 갖지 않는 함수들의 활용 중심의 관점을 갖는다.
기존 객체지향 프로그래밍에서 가지는 문제 해결 대안으로 함수형 프로그래밍이 주목받았다.
- 함수 비일관성
- 객체 멤버 변수가 변경될 경우 함수가 다른 결과를 반환할 수 있다.
- 객체 간 의존성 문제
- 객체 간 상호작용을 위해 다른 객체 함수를 호출하고, 이는 프로그램 복잡도를 증가시킨다.
- 객체 내 상태 제어 어려움
- 외부에서 객체 상태 변경 메서드들이 호출되면 언제 어디서 객체 상태를 변경했는지 추적하기 어렵다.
함수형 프로그래밍은 외부 상태를 갖지 않는 함수들의 연속적인 프로그래밍 패러다임이라고 보면 된다.
(여기서, 외부 상태를 갖지 않는 함수는 입력을 넣으면 항상 같은 출력을 내보내는 것을 뜻함)
한 번 초기화한 변수는 변하지 않는다. (불변성)
대표적인 언어로 스칼라(Scala)가 있다.
장점으로는, 상태를 갖지 않기 때문에 예측에서 벗어나는 결과(사이드 이펙트)가 없다. 사이드 이펙트가 없기 떄문에 안정적이고, 동시성을 갖는 프로그램에 사용하기 적합하다.
단점으로는, 실제로 상태를 가지지 않는 함수를 작성하고 활용하는 코드를 작성하는 것이 어렵다. 친숙하지 않은 설계 방식으로 러닝 커브가 높다.
객체지향의 기본적인 개념들 정리
- 클래스
- 객체를 만들기 위한 설계도
- 인스턴스
- 설계한 클래스를 통해 만들어지는 하나의 객체
- 인스턴스는 같은 클래스로부터 만들어지지만, 각자 다른 메모리 공간에 존재하고, 독립된 내용을 담을 수 있다.
- 속성(attribute)
- 클래스 내에 담기는 데이터들을 말하며 멤버 변수라도고 한다.
- 클래스 속성은 크게 클래스 변수와 인스턴스 변수로 나눌 수 있다.
- 인스턴스 변수
- 인스턴스 별로 독립적으로 가질 수 있는 값
- 보통 생성자 부분에 입력을 받아 인스턴스를 넣는다.
- self로 접근한다.
- 클래스 변수
- 클래스 자체에 정의되는 변수로, 인스턴스는 클래스 변수에 접근할 수 있다.
- ex) User.users_num = 1 처럼 `클래스.`으로 접근한다.
- 인스턴스 별로 독립된 메모리 공간을 갖는 인스턴스 변수와 달리, 클래스 변수는 같은 클래스의 인스턴스들이 공유한다.
- 인스턴스 변수
- 메서드
- 클래스가 가지고 있는 함수이자 객체가 할 수 있는 행위
- public 메서드와(외부에서 접근 가능한) private 메서드(클래스 내부에서만 사용하는)로 나눌 수 있다.
- 파이썬의 경우 완전한 객체지향 언어가 아니기 때문에 자바처럼 private, public으로 타입을 문법적으로 정의하여 제어하는 접근 제어자 문법이 없다. 그래서 변수나 메서드 이름 앞에 _(underscore)를 붙임으로써 비공개 변수, 메서드임을 명시한다.
- 상속
- 클래스의 기능을 그대로 물려받는 것
- 상속해주는 클래스를 “부모 클래스” 혹은 “기반 클래스”라고 하고, 상속받는 클래스를 “자식 클래스” 혹은 “파생 클래스”라고 한다.
- 인터페이스
- 객체의 행위를 정의한 것으로, 클래스 메서드의 명세라고 볼 수 있다.
- 인터페이스는 객체 뼈대만 담당하고 있어 그대로 인스턴스화 할 수 없다. 인터페이스 객체를 상속받는 구현 클래스도 구현해야 한다. (구현체)
- 인터페이스를 사용하는 이유?
- 클라이언트 입장에서 요청을 할 때 인터페이스만 알고 있으면 객체의 실제 구현체에 대해서는 알지 않아도 된다. 어떤 행위를 제공하는지 인터페이스만 보면 된다.
- 인터페이스와 구현체를 나뉘어져 있으면 구현(저수준 코드)을 의존하기보단 인터페이스(잘 바뀌지 않는 고수준 코드)에 의존하게 되어 결합도를 낮출 수 있다. (-> 다형성)
- 구현 클래스 간 관계를 파악하기 쉬워진다.
객체지향의 특성 알아보기
객체 지향 4대 특성으로 상속, 추상화, 다형성, 캡슐화가 있다.
책임과 협력
객체지향은 객체들의 책임과 협력으로 이루어진다.
- 책임
- 한 객체가 특정하게 수행해야 하는 범위와 기능
- SRP (Single Responsibility Principle)
- 하나의 객체는 하나의 책임만 가지도록 설계하는 것이 일반적으로 좋다고 알려져 있다.
- 객체 정체성이 명확하고, 변경에 용이하며, 추후 재사용 가능하고, 높은 응집도와 낮은 결합도를 유지할 수 있기 때문이다.
- 협력
- 정의한 기능 실제로 구현하는 부분
- 객체를 각자 책임에 맞게 설계하고, 객체가 서로 필요에 따라 의존하는 것을 협력이라 표현한다.
상속
상위 클래스의 기능을 하위 클래스가 사용할 수 있는 개념이다.
상속이 필요한 이유는 여러 객체에서 사용되는 기능을 하나의 클래스로 분리해서 사용할 수 있도록 하기 위함이다.
즉, 중복되는 코드의 재사용성을 위함이다.
추상화
추상화를 통해 객체들의 공통 개념을 뽑아내어 한 차원 더 높은 객체를 만들 수 있다.
추상화가 없을 때 확장하고 싶은 경우, if .. else 문을 남발하게 된다.
추상화란 구체적인 객체들로부터 공통점을 찾아 한 차원 높은 개념으로 만드는 것이다.
추상화하기 위해 추상클래스(인터페이스)를 만들고, 추상 클래스 내에 추상 메서드를 만든다.
추상화된 인터페이스의 역할인 구현체를 추상 클래스를 상속받아 구현한다.
다형성
다형성으로는 코드는 수정과 확장에 유연해진다.
객체가 생성자에서 추상클래스를 의존하고 있지만, 실제로 객체를 사용하는 클라이언트에서는 추상클래스의 자식 클래스인 구현체와 객체를 생성자에서 파라미터로 넘겨줄 수 있다.
즉, 상황에 따라 형태가 달라질 수 있는 특성을 다형성(Polymorphism)이라 하며, 이렇게 외부에서 실제로 의존하는 객체를 만들어 넘겨주는 패턴을 의존성 주입이라고 한다.
추상클래스 혹은 인터페이스로 객체를 상위 타입으로 추상화하고, 그 객체의 하위 타입들은 상위 타입의 추상클래스나 인터페이스를 구현하도록 하면, 코드 설계가 전반적으로 유연해져 수정과 확장이 매우 용이해진다.
캡슐화
캡슐화를 통해 객체 내부의 정보와 구체적인 로직을 외부에 숨길 수 있다.
객체 내부의 데이터나 메서드의 구체적인 로직을 외부에서 모르고 사용해도 문제가 없도록 하는 특성이다.
캡슐화를 하지 않은 경우 객체의 속성에 직접 접근하여 연산하게 된다. 클라이언트 입장에서 마음대로 컨트롤하게 되는 문제가 충분히 생길 수 있다. 버그에 취약한 코드가 되어버린다. (SRP 위반, 추후 코드 변화에도 매우 취약함)
캡슐화를 할 때, 외부에서 객체의 속성을 직접 수정하거나 가져다 쓰지 않도록 해야한다.
객체 밖에서는 정보나 로직을 모르기 때문에, 이를 정보 은닉이라고 부른다.
객체의 속성을 외부에서 사용하지 못하도록 퍼블릭 메서드를 하나 추가하여 구체적인 로직은 객체 외부에서 몰라도 되도록 해당 메서드 내에 둔다. 무조건 퍼블릭 메서드로 접근하게끔 하는 것이다.
'Devlog > CS' 카테고리의 다른 글
객체지향 5대 원칙 - SOLID 원칙 (0) | 2022.08.06 |
---|---|
테스트 코드와 TDD 이해하기 (0) | 2022.07.11 |
쿠키와 세션, 인증 이해하기 (0) | 2022.06.24 |
OSI 7계층과 TCP/IP 4계층 모델 (0) | 2022.06.22 |
프로그램 운영 기본 지식 (0) | 2022.06.21 |