본문 바로가기

길게 쓰기/객체지향의 탄생 (공식)

정의와 목표의 전환-한두깨 객체지향 프로그래밍 미리보기

한번 읽고 두번 깨닫는 객체지향 프로그래밍 미리보기


책이 2019년 1월말에 출간 예정인데 블로그 독자들에게 미리보기를 제공합니다.

보시고 댓글과 질문 많이 남겨주시면 감사합니다. +.+

 

1. 1발상의 전환

 

시작은 발상의 전환이다. 현장에서 우리는 주로 객체지향 언어를 사용한다. 서버는 역시 객체지향 언어인 JAVA를 주로 사용한다. 안드로이드도 JAVA를 쓰거나, 안드로이드 최신 언어 Kotlin은 함수형 프로그래밍과 객체지향을 함께 쓴다. 아이폰 개발은 Objective-C사용한다. 아이폰 최신 언어인 Swift도 함수형 프로그래밍과 함께 객체지향이 바탕이다. 한 마디로 여전히 객체지향이 주류다. 그런데 주변 개발자를 보면, 객체지향의 정의가 무엇인지, 어떤 목표를 지향하는지 잘 모르는 경우가 많다. 그래서 객체지향에 대한 동기부여조차 얻지 못하는, 의욕 없는 개발자를 많이 만난다. 결국 많은 개발자들이 내 마음대로 빠르게만 구현하는 기능 중심 개발로 돌아서(버리)는 경우가 많다. (게다가) 객체지향이라고 하면, 개발자들 나름대로 가지고 있는 오류나 고정관념도 많은 것 같다. 따라서 <1장 발상의 전환>에서 객체지향의 정의와 목표를 바로 세우고, 객체지향의 잘못된 고정관념을 해소하고자 한다. 예를 들어, 생성자, 메소드 등의 객체지향 구성 요소를 자격증 이론 암기하듯 공부할 수 있다. 그러나 객체지향이 추구하는 사고방식을 익히기는 어렵다. 우리는 보통 위에서 아래로 내려오면서 로직을 구현하는 절차형 사고 익숙하다. 하지만 객체지향은 사람이 사물을 보는 관점으로 접근 문제 해결하는 능력을 요구한다.

이를테면), 어렸을 때 물놀이를 하면 다들 자연스럽게 개헤엄을 익. 개헤엄으로 당장 수영할 수는 있지만 개헤엄은 느리고 폼도 안 난다. 이제 우리는 멋지고 우아한 수영 영법을 (제대로) 배우고 싶다. 그러나 한번 굳어진 폼을 고치기가 얼마나 어려운가?

우리는 기존에 익숙한 명령문의 흐름에 따라 기능 구현하는 방식을 뛰어넘어 아직 익숙하지 않지만 더 (유익한) 사람이 사물을 보는 관점의 객체지향 개발로 새롭게 진화해야 한다.

 

가. 1. 정의와 목표의 전환

 

나는 개발자들이 객체지향의 좋은 점을 잘 모르거나, 객체지향적 구현을 힘들어 하는 모습을 (자주) 지켜봤기 때문에, (우선) 객체지향 개발을 하면 체감할 수 있는 좋은 점부터 정리하여, 독자들이 객체지향의 장점을 실감나게 받아들이는 것이 중요하다고 본다. 목표를 확실히 알면, 의지를 갖고 완주하는 추진력을 얻을 수 있다. 행여 객체지향적으로 개발하기 힘든 바쁜 일정 등의 상황이 닥치더라도 포기하지 않고 객체지향적 개발 노력에 집중할 것이다.

 

1) 대부분의 개발자가 기능 중심 개발에 매달리는 이유

개발을 시작할 때, 개발자들과 고객의 관심분야는, 고객 요구사항의 온전한 기능 구현이다. 기능 구현이란 고객의 요구사항을 고스란히 소프트웨어로 구현하는 것이다. 프로그램의 기능 구현은 개발자의 당연한 임무다. 그러나 소프트웨어 개발자에게는 기능 구현만큼 중요하고 챙겨야 할 것이 있다. 예를 들면 유연한 소프트웨어와 빌드 자동화 같은 개발 환경이다. 그러나 많은 개발자들 기능 구현에만 신경 쓰고 있다는 게 문제다. 흔히 주어진 요구사항을 어떡하든 구현 돌아가게만 하자태도다. 역시 신입이었을 때, 빨리 실력 있는 개발자로 인정받고 싶었다. 문제가 주어지면 빠르게 버그 없이 구현하고 싶었다. 예를 들어 게시판 답글 달기, 페이징 기능을 구현하라는 지시를 받으면, 나는 수단과 방법을 가리지 않고 기능을 구현하는데 집중했다. 마치 물에 빠졌을 때 개헤엄이라도 빨리 빠져 나오는 처럼, 주어진 문제를 (오직) 기능 중심 구현으로 해결했다. 하지만 과연 이것이 초급 개발자만의 문제일까?

개발 경력 3년 이상 중급자를 한 번 살펴보자. 중급자는 크게 두 가지 그룹으로 나눌 수 있다. 첫 번째 소프트웨어의 기능 구현만 우선순위를 두는 경우다. 기능 구현이라는 목표를 위해 코드 품질(유연한 소프트웨어)은 크게 신경 쓰않는다. 두 번째 그룹은 객체지향의 장점을 알고 소프트웨어 유연성도 염두에 둔다. 그러나 우리나라 프로젝트는 거의 대부분 일정 압박이 심하다. 그러니 두 번째 중급자 그룹 역시 시간이 지날수록 기능 구현 중심으로 치우친다. 사실은 나도 워낙 바쁘다는 핑계로 이런 개발자가 되기도 했다. 당시 내 주변에서, 어떠한 상황에서도 객체지향적으로 우아하게(?) 개발하는 개발자는 10명 중에 1명도 보기 힘들었다. 이렇게 중급 개발자들 또한 원래 스타일이 기능 구현 중심적이거나 열악한 일정과 개발 환경 탓에 결국 기능 구현 중심으로 개발하게 된다. 이게 끝이 아니다.

모든 프로젝트 현장에는 개발을 의뢰한 고객이 있다. 고객은 개발자보다 훨씬 더 기능 구현 중심적이다. (당연한 말이지만) 고객은 원하는 기능이 제대로 구현되야 만족한다. 여기에 최대한 빨리라는 조건을 붙인다. 많은 고객사의 실무 담당자는 납기일 내 기능 구현을 프로젝트 성공 기준으로 판단. 고객은 원하는 기능이, 빠른 시간 안에 구현될 때 만족한다. 그래서 고객은 최대한 빨리 완벽한 기능 구현이라는 목표를 달성하기 위해 더욱더 개발자를 압박한다. 그러니 우리 개발자는 다시 기능 구현 중심으로 개발할 수밖에 없는 환경에 직면.

 

나. 2) 기능 중심 개발 → 코드 품질 저하

우리 중 일부는 여기 의문이 들 것 같다. 고객이 요구한대로 기능 구현에 집중하는 것이 뭐가 문제인가? 당연히 기능 구현을 가장 우선적으로 신경 써야 할 것 아닌가? 라고 말이다. 고객이 원하는 기능을 누락 왜곡 없이, 빠른 시간 내 구현하는 것은 당연하다. 객체지향적으로 개발해도 이것은 마찬가지다. 그러나 개발자라면 여기에 더 나아가, 좋은 소프트웨어를 만들기 위 관심을 갖고 적절한 노력을 추구해야 할 의무가 있다. 우리는 보통 이런 부분을 무시하면서 기능 구현만 집착한다.

 

(1) 기능 중심 개발 이 불러오는 나비 효과

개발자가 기능 구현만 집착할 경우 매몰현상이 발생한다. 한 가지만 뚫어지게 신경 쓰다 보니, 다른 중요한 것들을 보지 못하는 부작용이다. 예를 들어 공군 비행기 중에 한 대에 두 명의 조종사가 타도록 설계된 기종이 있다. 왜 굳이 두 명이 필요할까? 앞좌석 조종사는 비행기를 움직이는 일에 특화 임무를 수행한다. 예를 들어 고도 조절, 속도, 방향, 선회 등의 기체 조종을 담당하고, 만약 적기랑 마주치면 전투를 맡는다. 반면 뒷좌석 조종사는 (만약 정찰기라면) 정찰 업무를 한다. 비행기 바깥 상황을 관찰하고 고급 정보가 될 만한 장면들을 수집한다. 폭격기면 (미리) 폭격할 장소를 측정하고 정확하게 폭격하도록 계산한다. 만약 한 명의 조종사라면 조종이라는 임무에만 집중하다 다른 중요한 일을 하기 어렵다. 소프트웨어도 같다. 기능 구현만 집착하면, 더 중요한 다른 일(= 유지보수, 확장성 등)들은 신경 쓸 여력이 없어진다. 이것은 개발자가 일부러 의도하지 않았지만, 어쩔 수 없이 기능 중심 개발로 내몰리는 현상이다. 즉 눈앞의 기능 구현만 쫓을 때의 큰 문제점은, 유연한 소프트웨어를 지향하지 못하는 것에 그치지 않고, 소프트웨어에 해를 끼친다는 데 있다.

 

(2) 기능 중심 개발이 코드에 끼치는 영향

기능 개발에만 집착할 때 생기는 피해는 코드가 전반적으로 지저분해지면서 코드 품질이 떨어지는 것에 있다. 기능 개발 집착의 결과로 생기는 1단계 대표적인 코드 품질 저하 문제로는 다음과 같다.

 

코드의 중복

기능 중심 구현만 집착하다 보면 당장 기능 작동을 위해 썼던 코드를 다른 곳에 복사/붙여넣기(Copy&Paste)하는 일이 생긴다. 정신없이 프로젝트를 하다보면 이런 일이 자주 생긴다. 이렇게 구현해도 작동 되겠지만 문제의 불씨를 남긴다. 나중에 같은 기능을 하는 코드에 버그가 생기거나 수정할 일이 있으면, 관련 코드를 모두 찾아서 전부 다 똑같이 수정해야 한다. 그러나 시간이 지나면 이 코드가 다른 곳에도 있는지 기억조차하기 어렵다. 더구나 다른 개발자가 유지보수 개발을 이어 받았, 코드 중복이 있는 곳을 알기는 더욱 어렵다.

 

코드 속성의 과도한 노출

기능 중심 구현은 객체지향의 캡슐화 등 권장사항을 지킬 마음의 여유 주지 않는다. 그래서 한 코드(클래스)의 변수(속성)들을 다른 코드(클래스)에서 직접 접근하여 조작할 수 있는 잘못된 상황조차 쉽게 만들어진다.

 

코드 행동(메소드)의 과도한 노출

소프트웨어의 유연함을 위해서는, 모듈 간 결합도를 낮추기 위해, 외부(다른 클래스에)에 노출할 메소드와 감춰야할 메소드 구분해야한다. 만약 기능 중심 구현에 몰두한다면 이 규칙은 지키기 어렵다. 그 결과 메소드 개발에서 일관성이 없어진다.

 

코드 배치의 일관성이 없다

코드 배치는 코드의 속성과 메소드 그리고 각종 로직이 각각 제 역할에 맞게, 언어에서 권장하는 깔끔한 방식으로, 좋은 장소에 배치되었다는 뜻이다. 기능 중심 개발에서는 기능 구현을 위해 수단과 방법을 가리지 않다보니, 코드의 속성이나 메소드, 각종 로직 배치에 있어서 일관성이 없다. 예를 들어 메소드 내부에 있어야할 지역 변수가 전역 변수로 빠져 나와 있거나, 다른 메소드에 있어야할 로직이 엉뚱한 메소드에 있는 경우도 있다. 또한, 기능 중심으로 개발하다보면 메소드가 해야 할 역할은 생각하지 않고 한곳에 온갖 잡다한 로직을 다 집어넣기도 한다. 내가 보았던 어떤 메소드는 몇 천 라인이 넘어가는 경우도 있었다. 결국 1단계 코드 품질 저하 다음과 같이 2단계 문제 이어진다.

 

코드 가독성 저하

코드는 읽기 쉬워야 한다. 읽기 쉬워야 버그 잡기 쉽고, 기능 수정하기 쉽고, 확장 개발하기 수월하다. 가독성은 단순히 주석을 많이 작성하는 것만으로 부족하다. 객체지향적으로 속성과 메소드를 배치하고 메소드 로직 길이를 간결하게 유지할 때라야 가독성이 좋아진다. 그러나 기능 중심 개발만 몰두 하다보면 가독성을 지키기가 어렵다.

 

코드의 의존성이 증가한다

코드 속성이나 메소드가 과도하게 노출되면 다른 코드들이 쉽게 접근해서 지나치게 의존하게 된다. 그래서 의존성이 증가. 코드 배치에 일관성이 없으면 코드 의존성의 일관성 또한 같이 나빠진다. 1단계, 2단계 코드 품질 저하는 3단계 문제를 발생시킨다.

 

사이드 이펙트가 증가한다

사이드 이펙트는 하나의 코드를 수정했을 때, 수정한 코드와 연결된 다른 코드가 의도하지 않는 동작(=버그)을 일으키는 상황이다. 코드의 가독성이 안 좋고 코드 의존성이 증가하면 사이드 이펙트의 발생 가능성이 높아진다.

 

코드 재사용이 어렵다

소프트웨어에는 패키지별, 모듈별, 코드별, 메소드별로 개발된 코드들이 있다. 이와 같은 모듈화된 코드들이, 다른 요구사항, 다른 코드에서 재사용하기 쉬울 때, 우리 개발자들이 편해진다. 우리가 짠 모듈 여러 곳에 재사용 할 수 있다면, 코드는 깔끔해지고 개발비용 절감할 수 있다. 그러나 코드 품질이 저하 되면 코드 재사용도 어려워진다. 1단계, 2단계, 3단계 코드 품질 저하로 4단계 수정 작업이 어려워진다.

 

코드 수정, 추가 개발, 디버그가 어려워진다

소프트웨어가 개발된 그대로 수명을 다할 때까지 유지되는 경우는 거의 없다. 고객의 요구 사항은 자주 변경되고, 수시로 기능 확장을 요구하기 때문이다. 그래서 소프트웨어는 코드 수정이 수월해야 한다. 추가 개발, 디버그 또한 넓게 보면 수정 작업과 거의 같은 의미다. 코드 품질이 저하되면 수정작업을 할 때 가독성이 떨어져서 어디를 어떻게 수정해야 할지 알아내기 어렵고, 수정한 다음에 사이드 이펙트가 발생할 확률 높아진다. 또한, 코드 재사용이 어렵기 때문에 수정 확장할 곳에 비슷한 코드를 복사/붙여넣기 하게 되서 코드가 많이 중복된다.

이렇게 1단계, 2단계, 3단계, 4단계로 발생한 코드 품질 저하 문제는 결국 전반적인 소프트웨어 관리 문제로 이어진다.

 

유지보수와 고도화 프로젝트에 지장이 있다

고객은 프로젝트 기간을 소프트웨어 개발 비용에서 가장 중요한 요소로 인식한다. 개발자와(이 단어 삭제-김동헌) 고객은 프로젝트 기간에 자신의 역량과 에너지를 집중시켜 프로젝트를 수단과 방법을 가리지 않고 완성하려 한다. 그러나 그 다음은 그렇게까지 신경쓰지 않는 편이다. 만약 프로젝트 일정에 대한 고객의 관심 정도가 10이라면 유지보수는 2 정도 되는 것 같다. 여지껏 내가 겪은 대부분의 프로젝트가 이런 경우였다. 그러나 소프트웨어는 생명주기가 다 하는 몇 년 동안 계속 유지보수를 해야 한다. 소프트웨어 공학에서는 프로젝트를 진행하는 기간부터 유지보수성을 고려하지 않으면, 유지보수 단계에서 비용이 훨씬 증가한다고 강조한다. 현장에서 일해 보니 실제로 그렇다. 유지보수는 소프트웨어의 개발단계에서 가장 오랜 시간을 차지한다. 그래서 납기일도 중요하지만, 편하게 유지보수 할 수 있도록, 코드 품질을 좋게 하는 것도 하나의 기준으로 삼아야 한다. 그러나 코드 품질이 저하된 소프트웨어를 유지보수하면 개발자는 힘들어진다. 장기적으로 고객도 유지보수 효율성이 좋지 않다는 것을 알게 된다. 또한 유지보수를 하다보면 발전하는 기술 속도를 따라잡기 위한 고객 요구사항을, 유지보수 인력만으로 감당하기 힘든 때가 온다. 그때 고객은 아예 추가로 신규 프로젝트를 발주하기도 한다. 고도화 프로젝트라는 이름으로 소프트웨어의 디자인과 기능을 획기적으로 개선하고자 하는 때이다. 만약 기존 소프트웨어를 유연하게 개발해서 유지보수하고 있었다면, 더 나은 여건으로 추가 프로젝트를 할 수 있다. 그러나 기능 구현 개발에만 매몰될 경우 유지보수도 어렵고 고도화 프로젝트 비용도 더 들어가게 된다. 이렇게 기능 중심 개발은 비록 작동은 할지라도 그 안에서 악취가 풍긴다. 나중에는 개선하고 싶어도 이미 엉망으로 소프트웨어를 개발했기 때문에 고치기도 어렵다. 결국 유지보수도 어렵고 추가 프로젝트도 어렵다.

 

※ 기능 구현 중심 개발로 인한 코드 품질 저하 흐름도

개발자의 역량 부족 또는 열악한 환경 → 기능 구현 중심에 매몰 → 유연한 소프트웨어를 지향하지 못함에서 그치지 않고 해를 끼침 = 코드 중복 + 코드 속성의 과도한 노출 + 코드 메소드의 과도한 노출 + 코드 배치의 일관성이 없다. → 코드의 가독성 저하 + 코드의 의존성 증가 → 사이드 이펙트 증가 + 코드 재사용 어려움 → 코드의 수정, 확장, 디버그가 어려워진다. → 유지보수와 대규모 확장 개발하기 어려워진다.

 

※ 저자의 코멘트 : 저자가 제시한 코드 품질 저하의 인과 관계는 완벽하게 일치하지 않을 수 있으니, 느슨하게 읽어 주세요. 예를 들어 코드 중복 → 코드의 가독성 저하 같은 인과 관계는 절대적인 것이 아니고, 거의 동시에 발생하기도 할 것 입니다. 또는 반대로 코드의 가독성이 저하되서 코드 중복을 또 하게 되는 인과관계도 발생할 것입니다. 전체적으로 가볍게 코드 품질 저하의 흐름을 파악하면 좋을 것 같습니다. 이 절에서 등장한 코드 품질을 저하시키는 실제 코드 예제들은 이 책을 읽다보면 계속 등장합니다. 그러니 가볍게 이런 나쁜 코드들이 있다라는 것만 인식하고 넘어가셔도 좋습니다. 기능 구현을 열심히 하면서 코드 품질 신경 쓰는 개발자도 있습니다. 여기서 말하는 내용은, 우리 같은 평범한 개발자라면 기능 구현만 집착할 때 코드 품질까지 신경 쓰기가 어렵다는 것입니다.