Don't Panic
in CS ☕ 15분 읽기

[CS] 클린코드부터 MSA까지 — 소프트웨어 공학 핵심 정리

#CS
목차

클린코드와 리팩토링

클린코드와 리팩토링은 의미만 보면 비슷하다고 느껴진다. 어떤 차이점이 있을지 생각해보자.

클린코드

클린코드란, 가독성이 높은 코드를 말한다.

가독성을 높이려면 다음과 같이 구현해야 한다.

  • 네이밍이 잘 되어야 함
  • 오류가 없어야 함
  • 중복이 없어야 함
  • 의존성을 최대한 줄여야 함
  • 클래스 혹은 메소드가 한 가지 일만 처리해야 함

얼마나 코드가 잘 읽히는지, 코드가 지저분하지 않고 정리된 코드인지를 나타내는 것이 바로 ‘클린 코드’다.

public int AAA(int a, int b){
    return a+b;
}
public int BBB(int a, int b){
    return a-b;
}

두 가지 문제점이 있다.

public int sum(int a, int b){
    return a+b;
}

public int sub(int a, int b){
    return a-b;
}

첫째는 함수 네이밍이다. 다른 사람들이 봐도 무슨 역할을 하는 함수인지 알 수 있는 이름을 사용해야 한다.

둘째는 함수와 함수 사이의 간격이다. 여러 함수가 존재할 때 간격을 나누지 않으면 시작과 끝을 구분하는 것이 매우 힘들다.


리팩토링

프로그램의 외부 동작은 그대로 둔 채, 내부의 코드를 정리하면서 개선하는 것을 말한다.

이미 공사가 끝난 집이지만, 더 튼튼하고 멋진 집을 만들기 위해 내부 구조를 개선하는 리모델링 작업과 같다.

프로젝트가 끝나면 지저분한 코드를 볼 때 가독성이 떨어지는 부분이 존재한다. 이 부분을 개선시키기 위해 필요한 것이 바로 ‘리팩토링 기법’이다.

리팩토링 작업은 코드의 가독성을 높이고, 향후 이루어질 유지보수에 큰 도움이 된다.

리팩토링이 필요한 코드

  • 중복 코드
  • 긴 메소드
  • 거대한 클래스
  • Switch 문
  • 절차지향으로 구현한 코드

리팩토링의 목적은, 소프트웨어를 더 이해하기 쉽고 수정하기 쉽게 만드는 것이다.

리팩토링은 성능을 최적화시키는 것이 아니다. 코드를 신속하게 개발할 수 있게 만들어주고, 코드 품질을 좋게 만들어준다.

이해하기 쉽고, 수정하기 쉬우면 → 개발 속도가 증가!

리팩토링이 필요한 상황: 소프트웨어에 새로운 기능을 추가해야 할 때

명심해야 할 것은, 우선 코드가 제대로 돌아가야 한다는 것. 리팩토링은 우선적으로 해야 할 일이 아님을 명심하자.

객체지향 특징을 살리려면, switch-case 문을 적게 사용해야 한다. (switch문은 오버라이드로 대체하자)

리팩토링 예제

1번

// 수정 전
public int getFoodPrice(int arg1, int arg2) {
    return arg1 * arg2;
}

함수명 직관적 수정, 변수명을 의미에 맞게 수정

// 수정 후
public int getTotalFoodPrice(int price, int quantity) {
    return price * quantity;
}

2번

// 수정 전
public int getTotalPrice(int price, int quantity, double discount) {
    return (int) ((price * quantity) * (price * quantity) * (discount /100));
}

price * quantity가 중복된다. 따로 변수로 추출하자.

할인율을 계산하는 부분을 메소드로 따로 추출하자.

할인율 함수는 항상 일정하므로 외부에서 건드리지 못하도록 private 선언.

// 수정 후
public int getTotalFoodPrice(int price, int quantity, double discount) {
	int totalPriceQuantity = price * quantity;
    return (int) (totalPriceQuantity - getDiscountPrice(discount, totalPriceQuantity))
}

private double getDiscountPrice(double discount, int totalPriceQuantity) {
    return totalPriceQuantity * (discount / 100);
}

3번

// 수정 전
public int getTotalFoodPrice(int price, int quantity, double discount) {

    int totalPriceQuantity = price * quantity;
    return (int) (totalPriceQuantity - getDiscountPrice(discount, totalPriceQuantity))
}

private double getDiscountPrice(double discount, int totalPriceQuantity) {
    return totalPriceQuantity * (discount / 100);
}

totalPriceQuantity를 getter 메소드로 추출이 가능하다.

지불한다는 의미를 주기 위해 메소드 명을 수정하자.

// 수정 후
public int getFoodPriceToPay(int price, int quantity, double discount) {

    int totalPriceQuantity = getTotalPriceQuantity(price, quantity);
    return (int) (totalPriceQuantity - getDiscountPrice(discount, totalPriceQuantity));
}

private double getDiscountPrice(double discount, int totalPriceQuantity) {
    return totalPriceQuantity * (discount / 100);
}

private int getTotalPriceQuantity(int price, int quantity) {
    return price * quantity;
}

클린코드와 리팩토링의 차이

구분클린코드리팩토링
초점가독성 향상유지보수를 위한 코드 개선 전반
시점설계부터 잘 이루어져야 함결과물이 나온 이후 수정·추가 시
범위단순히 읽기 좋은 코드클린코드 포함 + 구조 개선

리팩토링이 더 큰 의미를 가진다. 클린코드는 단순히 가독성을 높이기 위한 작업이라면, 리팩토링은 클린코드를 포함한 유지보수를 위한 코드 개선이 이루어진다.


클린코드와 시큐어코딩

전문가들이 표현한 클린코드

“한 가지를 제대로 한다.”

“단순하고 직접적이다.”

“특정 목적을 달성하는 방법은 하나만 제공한다.”

“중복 줄이기, 표현력 높이기, 초반부터 간단한 추상화 고려하기 — 이 세 가지가 비결”

“코드를 읽으면서 짐작했던 기능을 각 루틴이 그대로 수행하는 것”

클린코드란?

코드를 작성하는 의도와 목적이 명확하며, 다른 사람이 쉽게 읽을 수 있어야 한다.

가독성을 높인다는 것은, 다른 사람이 코드를 봐도 자유롭게 수정이 가능하고 버그를 찾고 변경된 내용이 어떻게 상호작용하는지 이해하는 시간을 최소화시키는 것이다.

클린코드를 만들기 위한 규칙

1. 네이밍(Naming)

변수, 클래스, 메소드에 의도가 분명한 이름을 사용한다.

int elapsedTimeInDays;
int daysSinceCreation;
int fileAgeInDays;
  • 잘못된 정보를 전달할 수 있는 이름을 사용하지 않는다.
  • 범용적으로 사용되는 단어 사용 X (aix, hp 등)
  • 연속된 숫자나 불용어를 덧붙이는 방식은 피해야 함

2. 주석달기(Comment)

코드를 읽는 사람이 코드를 작성한 사람만큼 잘 이해할 수 있도록 도와야 한다.

주석은 반드시 달아야 할 이유가 있는 경우에만 작성한다. 코드를 빠르게 유추할 수 있는 내용에는 주석을 사용하지 않는 것이 좋다.

// 주어진 'name'으로 노드를 찾거나 아니면 null을 반환한다.
// 만약 depth <= 0이면 'subtree'만 검색한다.
// 만약 depth == N 이면 N 레벨과 그 아래만 검색한다.
Node* FindNodeInSubtree(Node* subtree, string name, int depth);

3. 꾸미기(Aesthetics)

보기 좋게 배치하고 꾸민다. 보기 좋은 코드가 읽기도 좋다.

  • 규칙적인 들여쓰기와 줄바꿈으로 가독성을 향상시키자.
  • 일관성 있고 간결한 패턴을 적용해 줄바꿈한다.
  • 메소드를 이용해 불규칙한 중복 코드를 제거한다.
  • 클래스 전체를 하나의 그룹이라고 생각하지 말고, 그 안에서도 여러 그룹으로 나누는 것이 읽기에 좋다.

4. 흐름제어 만들기(Making control flow easy to read)

  • 왼쪽에는 변수를, 오른쪽에는 상수를 두고 비교
if(length >= 10)

while(bytes_received < bytest_expected)
  • 부정이 아닌 긍정을 다루자
if( a == b ) { // a!=b는 부정
	// same
} else {
	// different
}
  • if/else를 사용하며, 삼항 연산자는 매우 간단한 경우만 사용
  • do/while 루프는 피하자

5. 착한 함수(Function)

함수는 가급적 작게, 한 번에 하나의 작업만 수행하도록 작성한다.

온라인 투표 예시 — vote_change(old_vote, new_vote) 함수를 호출한다고 가정해보자.

var vote_changed = function (old_vote, new_vote) {

	var score = get_score();

	if (new_vote !== old_vote) {
		if (new_vote == 'Up') {
			score += (old_vote === 'Down' ? 2 : 1);
		} else if (new_vote == 'Down') {
			score -= (old_vote === 'Up' ? 2 : 1);
		} else if (new_vote == '') {
			score += (old_vote === 'Up' ? -1 : 1);
		}
	}
	set_score(score);

};

총점을 변경해주는 한 가지 역할을 하는 함수같지만, 두 가지 일을 하고 있다.

  • old_vote와 new_vote의 상태에 따른 score 계산
  • 총점을 계산

별도 함수로 분리하여 가독성을 향상시키자.

var vote_value = function (vote) {

    if(vote === 'Up') {
        return +1;
    }
    if(vote === 'Down') {
        return -1;
    }
    return 0;

};

var vote_changed = function (old_vote, new_vote) {

    var score = get_score();

    score -= vote_value(old_vote); // 이전 값 제거
    score += vote_value(new_vote); // 새로운 값 더함
    set_score(score);
};

훨씬 깔끔한 코드가 되었다!


코드리뷰 & 리팩토링

레거시 코드(테스트가 불가능하거나 어려운 코드)를 클린 코드로 만드는 방법.

코드리뷰를 통해 냄새나는 코드를 발견하면, 리팩토링을 통해 점진적으로 개선해나간다.

코드 인스펙션(Code Inspection)

작성한 개발 소스 코드를 분석하여 개발 표준에 위배되었거나 잘못 작성된 부분을 수정하는 작업.

절차 과정

  1. Planning : 계획 수립
  2. Overview : 교육과 역할 정의
  3. Preparation : 인스펙션을 위한 인터뷰, 산출물, 도구 준비
  4. Meeting : 검토 회의로 각자 역할을 맡아 임무 수행
  5. Rework : 발견한 결함을 수정하고 재검토 필요한지 여부 결정
  6. Fellow-up : 보고된 결함 및 이슈가 수정되었는지 확인하고 시정조치 이행

리팩토링

냄새나는 코드를 점진적으로 반복 수행되는 과정을 통해 코드를 조금씩 개선해나가는 것

리팩토링 대상

대상설명
메소드 정리그룹으로 묶을 수 있는 코드, 수식을 메소드로 변경
객체 간의 기능 이동메소드 기능에 따른 위치 변경, 클래스 기능을 명확히 구분
데이터 구성캡슐화 기법을 적용해 데이터 접근 관리
조건문 단순화조건 논리를 단순하고 명확하게 작성
메소드 호출 단순화메소드 이름이나 목적이 맞지 않을 때 변경
클래스 및 메소드 일반화동일 기능 메소드가 여러 개 있으면 수퍼클래스로 이동

리팩토링 진행 방법

아키텍처 관점 시작 → 디자인 패턴 적용 → 단계적으로 하위 기능에 대한 변경으로 진행

의도하지 않은 기능 변경이나 버그 발생 대비해 회귀 테스트 진행. 이클립스와 같은 IDE 도구 이용.

시큐어 코딩

안전한 소프트웨어를 개발하기 위해, 소스코드 등에 존재할 수 있는 잠재적인 보안 약점을 제거하는 것

보안 약점을 노려 발생하는 사고 사례

  • SQL 인젝션 취약점으로 개인정보 유출 사고 발생
  • URL 파라미터 조작 개인정보 노출
  • 무작위 대입공격 기프트카드 정보 유출

SQL 인젝션 예시

안전하지 않은 코드:

String query "SELECT * FROM users WHERE userid = '" + userid + "'" + "AND password = '" + password + "'";

Statement stmt = connection.createStatement();
ResultSet rs = stmt.executeQuery(query);

안전한 코드:

String query "SELECT * FROM users WHERE userid = ? + "AND password = ?";

PrepareStatement stmt = connection.prepareStatement(query);
stmt.setString(1, userid);
stmt.setString(2, password);
ResultSet rs = stmt.executeQuery();

적절한 검증 작업이 수행되어야 안전하다. 입력받는 값의 변수를 $ 대신 #을 사용하면서 바인딩 처리로 시큐어 코딩이 가능하다.


TDD(Test Driven Development)

TDD : 테스트 주도 개발

‘테스트가 개발을 이끌어 나간다.’

우리는 보통 개발할 때, 설계(디자인)를 한 이후 코드 개발과 테스트 과정을 거치게 된다.

일반적인 개발 프로세스

하지만 TDD는 기존 방법과는 다르게, 테스트케이스를 먼저 작성한 이후에 실제 코드를 개발하는 리팩토링 절차를 밟는다.

TDD 개발 프로세스

작가가 책을 쓰는 과정에 대해서 생각해보자.

책을 쓰기 전, 목차를 먼저 구성한다.
이후 목차에 맞는 내용을 먼저 구상한 뒤, 초안을 작성하고 고쳐쓰기를 반복한다.

목차 구성 : 테스트 코드 작성
초안 작성 : 코드 개발
고쳐 쓰기 : 코드 수정(리팩토링)

반복적인 ‘검토’와 ‘고쳐쓰기’를 통해 좋은 글이 완성된다. 이런 방법을 소프트웨어에 적용한 것이 TDD!

소프트웨어 또한 반복적인 테스트와 수정을 통해 고품질의 소프트웨어를 탄생시킬 수 있다.

TDD 장단점

구분내용
장점작업과 동시에 테스트를 진행하면서 실시간 오류 파악 가능 (시스템 결함 방지)
짧은 개발 주기로 고객 요구사항 빠르게 수용. 피드백 가능, 진행 상황 파악 쉬움
자동화 도구를 이용한 TDD 테스트케이스를 단위 테스트로 사용 가능 (Java: JUnit, C/C++: CppUnit)
테스트 케이스가 앱 동작에 관한 문서 역할 수행 (코드와 함께 업데이트됨)
단점기존 개발 프로세스에 테스트케이스 설계가 추가되므로 생산 비용 증가
테스트 방향성, 프레임워크 선택 등 추가 고려 사항 증가

TDD 예제 — 점수 계산 프로그램

중간고사, 기말고사, 과제 점수를 통한 성적을 내는 프로그램. 90점 이상 A, 80점 이상 B, 70점 이상 C, 60점 이상 D, 나머지 F.

TDD 테스트케이스 먼저 작성. 35 + 25 + 25 = 85점 → 등급 B가 나와야 한다.

public class GradeTest {

    @Test
    public void scoreResult() {

        Score score = new Score(35, 25, 25); // Score 클래스 생성
        SimpleScoreStrategy scores = new SimpleScoreStrategy();

        String resultGrade = scores.computeGrade(score); // 점수 계산

        assertEquals("B", resultGrade); // 확인
    }

}

현재는 Score 클래스와 computeGrade() 메소드가 구현되지 않은 상태다. (테스트 코드로만 존재)

점수를 저장할 Score 클래스를 생성한다.

public class Score {

    private int middleScore = 0;
    private int finalScore = 0;
    private int homeworkScore = 0;

    public Score(int middleScore, int finalScore, int homeworkScore) {
        this.middleScore = middleScore;
        this.finalScore = finalScore;
        this.homeworkScore = homeworkScore;
    }

    public int getMiddleScore(){
        return middleScore;
    }

    public int getFinalScore(){
        return finalScore;
    }

    public int getHomeworkScore(){
        return homeworkScore;
    }

}

인터페이스를 구현하자.

public interface ScoreStrategy {

    public String computeGrade(Score score);

}

인터페이스를 가져와 오버라이딩한 클래스를 구현한다.

public class SimpleScoreStrategy implements ScoreStrategy {

    public String computeGrade(Score score) {

        int totalScore = score.getMiddleScore() + score.getFinalScore() + score.getHomeworkScore(); // 점수 총합

        String gradeResult = null; // 학점 저장할 String 변수

        if(totalScore >= 90) {
            gradeResult = "A";
        } else if(totalScore >= 80) {
            gradeResult = "B";
        } else if(totalScore >= 70) {
            gradeResult = "C";
        } else if(totalScore >= 60) {
            gradeResult = "D";
        } else {
            gradeResult = "F";
        }

        return gradeResult;
    }

}

이제 테스트 코드로 돌아가서, 실제로 통과할 정보를 입력해본 뒤 결과를 확인해보자. 예외 처리, 중복 제거, 추가 기능을 통한 리팩토링 작업을 통해 완성도 높은 프로젝트를 구현할 수 있도록 노력하자!

TDD 테스트 통과 화면

TDD를 활용하면, 처음 시작하는 단계에서 테스트케이스를 설계하기 위한 초기 비용이 확실히 더 들게 된다. 하지만 개발 과정에 있어서 ‘초기 비용’보다 ‘유지보수 비용’이 더 클 수 있다는 것을 명심하자.

안전성이 필요한 소프트웨어 프로젝트에서는 개발 초기 단계부터 확실하게 다져놓고 가는 것이 중요하다. 유지보수 비용이 더 크거나 비행기, 기차에 필요한 소프트웨어 등 안전성이 중요한 프로젝트의 경우 현재 실무에서도 TDD를 활용한 개발로 이루어지고 있다.


애자일(Agile)

소프트웨어 개발 기법으로 많이 들어본 단어다. 폭포수 모델, 애자일 기법 등등 무엇인지 알아보자.

등장 배경

초기 소프트웨어 개발 방법은 계획 중심의 프로세스였다. 마치 도시 계획으로 건축에서 사용하는 방법과 유사하며, 당시에는 이런 프로세스를 활용하는 프로젝트가 대부분이었다.

90년대 이후, 소프트웨어 분야가 넓어지면서 소프트웨어 사용자들이 ‘일반 대중들’로 바뀌기 시작했다. 트렌드가 급격하게 빨리 변화하는 시대가 도래하면서 비즈니스 사이클(제품 수명)이 짧아지고, SW 개발의 불확실성이 높아지게 되었다.

개발의 불확실성이 높아지면서, 옛날의 전통적 개발 방법 적용이 어려워졌고, 경량 방법론 주의자들은 “일단 해보고 고쳐나가자”는 방식으로 개발하게 되었다.

이런 경량 방법론 주의자들이 모여 자신들이 사용하는 개발 방법론을 공유하고, 공통점을 추려서 애자일이라는 용어에 의미가 담기게 된 것이다.

애자일이란?

애자일 개념도

협력과 피드백을 더 자주하고, 일찍하고, 잘하는 것!

애자일의 핵심은 바로 ‘협력’‘피드백’ 이다.

1. 협력

소프트웨어를 개발한 사람들 안에서의 협력(직무 역할을 넘어선 협력)을 말한다.

ex) 좋은 일은 x2가 된다.

어떤 사람이 2배의 속도로 개발할 수 있는 방법을 발견함

협력이 약하면? → 혼자만 좋은 보상과 칭찬을 받음. 하지만 그 사람 코드와 다른 사람의 코드의 이질감이 생겨서 시스템 문제 발생 가능성

협력이 강하면? → 다른 사람과 공유해서 모두 같이 빠르게 개발하고 더 나은 발전점을 찾기에 용이함. 팀 전체 개선이 일어나는 긍정적 효과 발생
ex) 안 좋은 일은 /2가 된다.

문제가 발생하는 부분을 찾기 쉬워짐
예상치 못한 문제를 협력으로 막을 수 있음

실수를 했는데 어딘지 찾기 힘들거나, 개선점이 생각나지 않을 때 서로 다른 사람들과 협력하면 새로운 방안이 탄생할 수도 있음

2. 피드백

학습의 가장 큰 전제조건이 ‘피드백’. 내가 어떻게 했는지 확인하면서 학습을 진행해야 한다.

소프트웨어의 불확실성이 높을수록 학습의 중요도는 올라간다. (모르는 게 많으면 더 빨리 배워나가야 하기 때문)

불확실성 대처 방식 비교

[전통적 방법론]
: '그때 계획 세울 때 좀 더 잘 세워둘껄..
이런 리스크도 생각했어야 했는데ㅠ 일단 계속 진행하자'

[애자일 방법론]
: '이건 생각 못했네. 어쩔 수 없지. 다시 빨리 수정해보자'

전통적 방법에 속하는 폭포수 모델(Waterfall Model) 은 요구분석 단계에서 한 번에 모든 요구사항을 정확하게 전달하는 것이 원칙이다. 하지만 요즘같이 변화가 많은 프로젝트에서는 현실적으로 불가능에 가깝다.

애자일은 개발 과정에서 시스템 변경사항을 유연하게·기민하게 대응할 수 있도록 방법론을 제공한다.

진행 방법

  1. 개발자와 고객 사이의 지속적 커뮤니케이션을 통해 변화하는 요구사항을 수용한다.
  2. 고객이 결정한 사항을 가장 우선으로 시행하고, 개발자 개인의 가치보다 팀의 목표를 우선으로 한다.
  3. 팀원들과 주기적인 미팅을 통해 프로젝트를 점검한다.
  4. 주기적으로 제품 시현을 하고 고객으로부터 피드백을 받는다.
  5. 프로그램 품질 향상에 신경 쓰며 간단한 내부 구조 형성을 통한 비용절감을 목표로 한다.

스크럼(Scrum)

애자일을 통한 가장 많이 사용하는 개발 방법론이 스크럼이다.

럭비 경기에서 반칙으로 인해 경기가 중단됐을 때 쓰는 대형을 말하며, 소프트웨어 측면에서 팀이라는 단어가 주는 의미를 적용시킨 효율적인 성과를 얻기 위한 방법론이다.

스크럼 프로세스

단계설명
1. 제품 기능 목록우선순위가 매겨진 사용자의 요구사항 목록. 한 주기가 끝날 때까지는 수정하지 않는 것이 원칙
2. 스프린트 Backlog스프린트 목표에 도달하기 위해 필요한 작업 목록 (세부 구현 내용, 작업자, 예상 작업 시간)
3. 스프린트작은 단위의 개발 업무를 단기간(2~4주) 내에 전력질주하여 개발. 목표와 내용은 팀원들 동의 없이 변경 불가
4. 일일 스크럼 회의모든 팀원 참석, 매일 15분, 어제 한 일 / 오늘 할 일 / 문제점 공유. 완료된 작업 스프린트 현황판 업데이트
5. 스프린트 검토 회의모든 스프린트 주기 종료 후 고객 앞 시연. 요구사항 부합 여부 및 피드백 확인
6. 스프린트 회고수행 활동을 되돌아보며 개선점·규칙 준수 여부 검토. 팀의 강점 극대화에 초점

스크럼 장점

  • 스프린트마다 실행 가능한 제품을 통해 사용자와 의견을 나눌 수 있음
  • 회의를 통해 팀원들간 신속한 협조와 조율이 가능
  • 자신의 일정을 직접 발표함으로써 업무 집중 환경 조성
  • 프로젝트 진행 현황을 통해 신속하게 목표와 결과 추정 가능, 변화 시도 용이

스크럼 단점

  • 추가 작업 시간이 필요함 (스프린트마다 테스트 제품을 만들어야 하기 때문)
  • 15분이라는 회의 시간을 지키기 힘듦 (시간이 초과되면 그만큼 작업 시간이 줄어듦)
  • 프로젝트 관리에 무게중심을 두기 때문에 프로세스 품질 평가에는 미약함

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

보통 OOP(Object-Oriented Programming) 라고 많이 부른다.

패러다임의 발전 과정

패러다임특징문제점
순차적·비구조적goto문을 활용한 순차 코딩규모가 커지면 goto문이 베베 꼬여 흐름 파악 불가
절차적·구조적반복되는 코드를 함수(프로시저)로 만들어 사용함수와 자료형을 따로 관리해야 해서 비효율적
객체지향함수(메소드)와 자료형(필드)를 객체로 묶어 관리

프로시저: 반환값(리턴)이 따로 존재하지 않는 함수. printf와 같이 화면에 출력하는 용도로 쓰이는 함수.

도서관리 프로그램이 있다고 가정해보자.
책에 해당하는 자료형(필드)를 구현해야 한다.
또한 책과 관련된 함수를 구현해야 한다.
구조적인 프로그래밍에서는 이들을 따로 만들어야 한다.
결국 많은 데이터를 만들어야 할 때, 구분하기 힘들고 비효율적으로 코딩할 가능성이 높아진다.

객체지향으로 구현하게 되면, 객체 간의 독립성이 생기고 중복코드의 양이 줄어드는 장점이 있다. 또한 독립성이 확립되면 유지보수에도 도움이 된다.

OOP 4가지 특징

1. 추상화(Abstraction)

필요로 하는 속성이나 행동을 추출하는 작업

추상적인 개념에 의존하여 설계해야 유연함을 갖출 수 있다. 세부적인 사물들의 공통적인 특징을 파악한 후 하나의 집합으로 만들어내는 것이 추상화다.

ex. 아우디, BMW, 벤츠는 모두 '자동차'라는 공통점이 있다.

자동차라는 추상화 집합을 만들어두고, 자동차들이 가진 공통적인 특징들을 만들어 활용한다.

다른 자동차 브랜드가 추가될 때 다른 곳의 코드는 수정할 필요 없이 추가로 만들 부분만 새로 생성해주면 된다.

2. 캡슐화(Encapsulation)

낮은 결합도를 유지할 수 있도록 설계하는 것

한 곳에서 변화가 일어나도 다른 곳에 미치는 영향을 최소화시키는 것. (객체가 내부적으로 기능을 어떻게 구현하는지 감추는 것)

결합도(coupling): 어떤 기능을 실행할 때 다른 클래스나 모듈에 얼마나 의존적인가를 나타내는 말.

소프트웨어 공학에서 객체 안의 모듈 간의 요소가 밀접한 관련이 있는 것으로 구성하여 응집도를 높이고 결합도를 줄여야 요구사항 변경에 대처하는 좋은 설계 방법이다.

캡슐화는 정보 은닉(Information Hiding) 을 활용한다. 외부에서 접근할 필요가 없는 것들은 private으로 접근하지 못하도록 제한을 두는 것이다.

3. 일반화(Generalization) / 상속(Inheritance)

여러 개체들이 지닌 공통된 특성을 부각시켜 하나의 개념이나 법칙으로 성립하는 과정

일반화(상속)은 또 다른 캡슐화다. 자식 클래스를 외부로부터 은닉하는 캡슐화의 일종이다.

상속 재사용의 단점

  1. 상위 클래스 변경이 어려워진다 — 부모 클래스를 의존하는 자식 클래스가 많을 때, 부모 클래스 변경 시 자식 클래스들이 영향을 받는다.
  2. 불필요한 클래스가 증가할 수 있다 — 유사기능 확장 시 필요 이상의 불필요한 클래스를 만들어야 하는 상황이 발생할 수 있다.
  3. 상속이 잘못 사용될 수 있다 — 같은 종류가 아닌 클래스의 구현을 재사용하기 위해 상속을 받게 되면, 부모 클래스와 IS-A 관계가 아닐 때 문제가 발생한다.

해결책 — 객체 조립(Composition)

필드에서 다른 객체를 참조하는 방식으로 구현된다. 변경 시 유연함을 확보하는 데 장점이 매우 크다.

  • 상속을 사용하는 경우: IS-A 관계가 성립할 때, 재사용 관점이 아닌 기능의 확장 관점일 때

4. 다형성(Polymorphism)

서로 다른 클래스의 객체가 같은 메시지를 받았을 때 각자의 방식으로 동작하는 능력

객체 지향의 핵심이다. 부모 클래스의 메소드를 자식 클래스가 오버라이딩해서 자신의 역할에 맞게 활용하는 것이 다형성이다.

객체 지향 설계 과정

  1. 제공해야 할 기능을 찾고 세분화한다. 그리고 그 기능을 알맞은 객체에 할당한다.
  2. 기능을 구현하는데 필요한 데이터를 객체에 추가한다.
  3. 그 데이터를 이용하는 기능을 넣는다.
  4. 기능은 최대한 캡슐화하여 구현한다.
  5. 객체 간에 어떻게 메소드 요청을 주고받을지 결정한다.

객체 지향 설계 원칙 — SOLID

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

함수형 프로그래밍

순수 함수를 조합하고 공유 상태, 변경 가능한 데이터 및 부작용을 피해 소프트웨어를 만드는 프로세스

함수형 프로그래밍 개념

‘선언형’ 프로그래밍으로, 애플리케이션의 상태는 순수 함수를 통해 전달된다.

명령형 vs 선언형

구분설명
명령형 프로그래밍 (절차지향, 객체지향)상태와 상태를 변경시키는 관점에서 연산을 설명. 알고리즘을 명시하고, 목표는 명시하지 않음
선언형 프로그래밍 (함수형)How보다는 What을 설명하는 방식. 알고리즘을 명시하지 않고 목표만 명시함

명령형 프로그래밍은 어떻게 할지 표현하고, 선언형 프로그래밍은 무엇을 할 건지 표현한다.

함수형 프로그래밍의 핵심 개념

개념설명
순수 함수 (Pure functions)반드시 하나 이상의 인자를 받고, 받은 인자를 처리해 반드시 결과물을 돌려줘야 함. 인자 외 다른 변수 사용 금지
합성 함수 (Function composition)여러 함수를 조합해 사용
공유 상태 피하기 (Avoid shared state)공유 상태를 사용하지 않음
상태 변화 피하기 (Avoid mutating state)원본 데이터는 절대 불변
부작용 피하기 (Avoid side effects)프로그래머가 바꾸고자 하는 변수 외에는 변경되면 안 됨

대표적인 자바스크립트 함수형 프로그래밍 함수: map, filter, reduce

함수형 프로그래밍 예시

var arr = [1, 2, 3, 4, 5];
var map = arr.map(function(x) {
  return x * 2;
}); // [2, 4, 6, 8, 10]

arr을 넣어서 map을 얻었음. arr을 사용했지만 값은 변하지 않았고 어떠한 부작용도 낳지 않음 — 순수 함수의 예시.

var arr = [1, 2, 3, 4, 5];
var condition = function(x) { return x % 2 === 0; }
var ex = function(array) {
  return array.filter(condition);
};
ex(arr); // [2, 4]

이는 순수 함수가 아니다. ex 메소드에서 인자가 아닌 condition을 사용했기 때문이다.

순수 함수로 고치면:

var ex = function(array, cond) {
  return array.filter(cond);
};
ex(arr, condition);

순수 함수로 만들면, 에러를 추적하는 것이 쉬워진다. 인자에 문제가 있거나 함수 내부에 문제가 있거나 둘 중 하나일 수밖에 없기 때문이다.


Java에서의 함수형 프로그래밍

Java 8이 릴리즈되면서, Java에서도 함수형 프로그래밍이 가능해졌다.

  • 부수효과: 주어진 값 이외의 외부 변수 및 프로그래밍 실행에 영향을 끼치지 않아야 된다는 의미

Java에서 활용할 수 있는 함수형 프로그래밍: 람다식, Stream API, 함수형 인터페이스

import java.util.Arrays;
import java.util.List;

public class stream {

	public static void main(String[] args) {
		List<String> myList = Arrays.asList("a", "b", "c", "d", "e");

        // 기존방식
        for(int i=0; i<myList.size(); i++){
            String s = myList.get(i);
            if(s.startsWith("c")){
                System.out.println(s.toUpperCase());
            }
        }

        // stream API를 이용한 방식
        myList.stream()
              .filter(s -> s.startsWith("c"))
              .map(String::toUpperCase)
              .forEach(System.out::println);

	}

}

단순히 함수를 선언해서 데이터를 내가 원하는 방향으로 처리해나가는 함수형 프로그래밍 방식을 볼 수 있다.


데브옵스(DevOps)

  • Development + Operations의 합성어

소프트웨어 개발자와 정보기술 전문가 간의 소통, 협업 및 통합을 강조하는 개발 환경이나 문화를 의미한다.

목적: 소프트웨어 제품과 서비스를 빠른 시간에 개발 및 배포하는 것

데브옵스의 개념은 애자일 기법과 지속적 통합(Continuous Integration)의 개념과도 관련이 있다.

관련 개념설명
애자일 기법실질적인 코딩을 기반으로 일정한 주기에 따라 지속적으로 프로토타입을 형성하고, 필요한 요구사항을 파악하며 즉시 수정사항을 적용하여 소프트웨어를 개발하는 적응형 개발 방법
지속적 통합통합 작업을 초기부터 계속 수행해서 지속적으로 소프트웨어의 품질 제어를 적용하는 것

써드 파티(3rd Party)란?

간혹 써드 파티라는 말을 종종 볼 수 있다. 경제 용어가 IT에서 쓰이는 부분이다.

하드웨어 생산자와 소프트웨어 개발자의 관계를 나타낼 때 사용하며, 그 중에서 서드 파티는 프로그래밍을 도와주는 라이브러리를 만드는 외부 생산자를 뜻한다.

구분설명
퍼스트 파티 개발자하드웨어 생산자가 ‘직접’ 소프트웨어를 개발하는 경우
세컨드 파티 개발자하드웨어 생산자인 기업과 자사간의 관계(또는 하청업체)에 속한 소프트웨어 개발자
서드 파티 개발자아무 관련없는 제3자 소프트웨어 개발자

편한 개발을 위해 플러그인이나 라이브러리 혹은 프레임워크를 사용하는데, 이처럼 제3자로 중간다리 역할로 도움을 주는 것이 서드 파티로 볼 수 있고, 이런 것을 만드는 개발자가 서드 파티 개발자다.


마이크로서비스 아키텍처(MSA)

MSA(Micro Service Architecture)는 소프트웨어 개발 기법 중 하나로, 어플리케이션 단위를 ‘목적’으로 나누는 것이 핵심이다.

Monolithic vs MSA

구분MonolithicMSA
구조모든 기능을 하나의 어플리케이션에 구성기능(목적)별로 컴포넌트를 나누고 조합
장점개발·환경설정 간단, 소규모 프로젝트에 유리서비스별 독립 배포, 부분 확장 가능
단점빌드/테스트 시간 증가, 하나의 문제가 전체에 영향, 확장성 불리API 통신으로 속도 느림, DB 트랜잭션 묶기 힘듦

Monolithic 구조

MSA 구조

MSA에서 각 컴포넌트는 API를 통해 다른 서비스와 통신하며, 모든 서비스는 각각 독립된 서버로 운영·배포하기 때문에 서로 의존성이 없다.

정답이 정해져 있는 것이 아니라, 프로젝트 목적, 현재 상황에 맞는 아키텍처 방식이 무엇인지 설계할 때부터 잘 고민해서 선택하자.