try anything chris!

육각형 개발자 본문

review

육각형 개발자

뭐든창하 2024. 7. 28. 17:06
728x90

 

주니어를 벗어나려는 후배님들에게 내 경험담을 기반으로 조언을 해주는게 충분하지 않을 것 같고, 그래도 공통적인 사항이라던가 정리가 일목요연하게 된 내용들을 가지고 얘기해줘야 도움이 될 것 같아서 보게 되었다.

 

목적은 그렇게 다른 이들에게 도움이 될 만한 내용을 전달하고자 읽기 시작했지만, 개인적으로도 많은 부분에서 도움이 되었다. 그전에는 글로써 명확하게 전달하기 어려운, 나만의 언어로 나만이 가지고 있던 개념이나 생각들을 잘 정리된 개념적인 용어와 문장으로 표현된 부분들을 볼 때마다 가려운 곳을 긁어주는 듯 했고 이제 어떻게 표현해야 잘 전달 될 수 있을지 도움이 많이 되었다.  이 글에도 나오는 내용이지만 계속 시간을 내서 글을 써봐야 실력이 느는것 처럼, 책도 계속 봐야 단어나 표현에  써먹을 재료들이 많아지고 개념들이 잘 머리속에 정립되서 입에서 술술 나오게 되는 것 같다.

 

후배님들에게 전해줄 좋은 내용들을 잘 정리하긴 했지만...내가 못지키고 있는 것들도 있어 약간은 부끄러운 부분들도 있었다.

그래도 후배님들이 나보다 더 좋은 개발자가 되고 더 좋은 시니어의 길을 걷길 바라는 마음에 정리한 것들 중 일부분은 이곳에 남겨놓고 나머지는 잘 전달해볼 예정이다.

 

구현 기술 적용

어떤 기술을 도입할 때는 보수적으로 고민하고 다음과 같은 내용을 신경 써야 한다.
# 신뢰 구축
먼저 동료한테 신뢰받아야 하며, 구성원의 공감을 얻기 위해서는 자신의 역량을 증명해야 한다. 먼저 기존 시스템을 이해하고 주어진 일을 제대로 수행해야 동료에게 신뢰를 얻을 수 있고, 신뢰가 쌓여야 내 주장에 힘이 실린다.

# 함께 할 동료
새로 적용하고자 하는 기술에 대해 함께 논의하고 공감대를 형상할 수 있는 동료가 꼭 필요하다. 내가 맞는다고 생각하는 의견에 같이 공감해 줄 동료가 없다면 내 제안은 그저 고집일 수도 있다.

# 타당성
이유가 타당해야 한다. 구현 기술로 해결하고자 하는 문제가 분명해야 한다. 명확한 목적이 없다면 그저 빛 좋은 개살구일 뿐이다. 조직이 처한 상황고 조건이 부합할 때 새로운 기술을 적용해야 그 기술의 장점이 빛을 발한다. 단순히 개인의 욕구 충족을 위해 구현 기술을 적용하려 한다면 새로운 기술 사용에 따른 비용과 시간 그리고 부담만 증가할 뿐이다.

# 점진적 적용
한 번에 다 바꾸겠다는 생각은 위험하다. 비핵심 영역에서 먼저 검증한 뒤 핵심 영역에 적용해도 늦지 않다.

# 시장 상황
시장 상황을 고려해야 한다. 도입하려는 기술에 능숙한 인력을 원활히 채용할 수 있어야 서비스를 지속할 수 있다. 함께 일하는 동료의 이력도 고려해야 한다.

 먼저 동료에게 신뢰를 받아야 한다! 이 말에 격하게 공감한다...

 

응집도

좋은 코드, 좋은 설계, 패턴, 아키텍처는 높은 응집도와 낮은 결합도를 추구한다.

응집도란, 모듈 안에 있는 요소가 함께 모여 있는 정도를 나타낸다.
왜 응집도를 높여야 할까? 결국 수정비용과 관련이 있다. 코드 분석 시간을 줄여주고, 기능을 변경할 때 수정할 범위도 줄어든다.
응집도는 역할 또는 책임과 관련이 있다. 응집도가 높아지면 구성 요소가 역할에 따라 알맞게 분리될 가능성이 커진다. 구성 요소가 역할에 따라 분리될수록 소프트웨어를 수정해야 할 때 변경 범위가 좁아진다. 또한 응집도가 높아질수록 구성 요소를 수정하려는 이유도 하나로 줄어든다.
단일 책임 원칙(Single Responsibility Principle)은 각 구성 요소는 하나의 책임만 가져야 한다는 원칙이다. 다르게 표현하면 구성 요소를 수정할 이유는 하나여야 한다.

# 캡슐화를 통한 응집도 높이기
/***************
 * before
 ***************/
Member m = getMember(id);
if (m.getExpiry().isBefore(LocalDate.now()) { // 만료여부 판단
    ...
}

// 만약 만료여부에 null 조건이 체크 추가 필요하면, 이 변경을 모두 찾아다니면서 수정 필요
if (m.getExpiry() != null && m.getExpiry().isBefore(LocalDate.now()) { // 만료여부 판단
    ...
}

/***************
 * after
 ***************/
Member m = getMember(id);
if (m.isExpired()) { // 만료여부 판단, null 체크 조건이 추가되어도 변경되지 않음
    ...
}

public class Member {
    ...

    public boolean isExpired() {
        return expiry.isBeofre(LocalData.now());
        
        // 만약 null 체크 조건이 추가되는 경우, 아래와 같이 한 곳에서만 변경
        // return expiry != null && expiry.isBeofre(LocalData.now());
    }   
}

 

결합도

소프트웨어 모듈이 서로 의존하는 정도(얼마나 밀접하게 연결되어 있는지 모듈 간 관계 정도를 나타냄)
한 요소를 수정할 때 다른 요소도 함께 수정해야 하면 두 요소 간 결합도가 높다고 표현한다. 결합도가 높아지면 유지보수 비용이 증가한다. 따라서 수정 비용을 낮추기 위해서는 응집도는 높이고 결합도는 낮춰야 한다. 응집도가 높다고 해서 반드시 결합도가 낮아지는 것이 아니다. 응집도를 높이려면 코드를 역할에 따라 분리해야 한다. 그러나 분리된 요소 간에 의존이 발생하게 되고, 서로 의존하는 정도가 올라갈수록 결합도가 증가한다. 캡슐화는 구현을 감춤으로써 두 구성 요소 간의 상호 작용을 줄여주어 응집도를 높이는 동시에 결합도를 낮춰준다.

# 추상화 타입과 결합도
/***************
 * before
 ***************/
public class MemberRegister {
    private JdbcTemplate jdbcTemplate;
    ....

    public void register(RegisterRequest req) {
        validate(req);
        Member m = createPendingMember(req);
        saveMember(m);
        sendNotiSms(m);
    }

    ...

    private void sendNotiSms(Member m) {
        String content = "...";
        jdbcTemplate.update(
        "insert into SMS_SEND(PHONE, CONTENT) values (?,?)", m.getPhone(), content
        );
    }
}
 
/***************
 * after
 ***************/
public class MemberRegister {
    // 구현채가 아닌 추상화 타입에 의존, Notifier 구현을 변경해도 MemberRegister 는 수정하지 않는다.
    private Notifier notifier; 
    ....

    public void register(RegisterRequest req) {
        validate(req);
        Member m = createPendingMember(req);
        saveMember(m);
        sendNotiSms(m);
    }

    ...

    private void sendNotiSms(Member m) {
        notifier.notifyTo(m);
    }
}

# 이벤트과 결합도
public class MemberRegister {
    public void register(RegisterRequest req) {
        validate(req);
        Member m = createPendingMember(req);
        saveMember(m);

        // 이벤트 발생
        // 이벤트를 사용한 코드에서 MemberRegister 클래스가 더 이상 통지에 대한 코드에 의존하지 않음
        Events.raise(new MemberRegisteredEvent(m));
    }
    ...
}

# 추상화.이벤트와 코드 추적
결합도를 낮추기 위해 추상화 타입을 중간에 위치시키거나 이벤트를 사용하면 두 코드를 직접 연결하지 않고 간접적으로 연결하게 된다. 두 코드가 간접적으로 연결되어 있기 때문에 직접 연결된 코드에 비해 코드를 분석하는 데 더 많은 노력이 들어간다. 따라서 추상화나 이벤트를 적용할 때는 결합도 감소, 응집도 증가, 변경 비용 감소, 테스트 가능성 등 얻을 수 있는 이점을 따져봐야 한다. 이점이 별로 없다면 추상화 타입을 사용하지 않고 구현만 분리해서 응집도를 높이는 것도 좋은 선택이 될 수 있다.

# 결합 유형
* 공통 결합(common coupling)
여러 모듈이 동일한 글로벌 데이터에 접근할 때 발생. 글로벌 데이터에서 변경이 발생하면 예측하기 힘든 문제가 생길 수 있다.

* 제어 결합(control coupling)
파라미터로 전달받은 값에 따라 동작이 달라진다. 무엇을 할지를 전달하는 형태로 흐름을 제어하는데 보통 파라미터를 사용해서 정보를 전달한다. 제어 결합은 내부 동작 방식을 외부에 노출해서 결합도를 높인다.

* 하위 클래스 결합
하위 클래스와 상위 클래스 간의 관계. 상위 클래스 기능을 사용하는 하위 클래스가 많을 수록 상위 클래스를 수정하기 어려워진다.
상속보다는 조립 원칙(Composition over inheritance)을 통해 결합도를 낮출 수 있다.

* 시간적 결합(temporal coupling)
단지 함께 실행해야 하므로 두 기능을 한 모듈에 묶을 때 발생한다. 예로 회원가입시 데이터의 저장과 문자 전송 기능을 함께 실행하기 위해 회원 가입 기능에 묶여 있다. 지켜야 하는 실행 순서도 시간적 결합에 해당한다.

* 논리적 결합(logical coupling) 또는 변경 결합(change coupling)
두 모듈 간의 변경 패턴이 존재할 때 발생한다. 모듈/시스템 데이터를 변경할 때 다른 모듈/시스템 데이터도 함께 변경해야 한다면 논리 결합이 발생한다. 예로 회원시스템의 회원 이메일 주소를 변경했을 때, 포인트 시스템의 이메일 주소도 함께 변경해야 하면 논리적으로 결합하고 있다고 할 수 있다.

 

리팩토링

# 수정 공포와 변경 비용
레거시는 수정하기 어렵다. 개발자는 왜 레거시에 부담을 느낄까?
* 긴 클래스, 긴 메서드, 복잡한 코드, 이상한 이름, 많은 중복, 테스트 코드 없음..
* 레거시 코드 수정과 악순환
   - 레거시 코드를 이해하는데 많은 노력 필요
   - 이해 부족 상태에서 코드 수정해야 함
   - 변경에 대한 두려움
   - 코드 덧데기로 두려움 회피 → (다시 코드 이해부터 반복)

레거시는 폄하 대상이 아니다.
레거시가 있었기에 서비스가 굴러가고 수익이 난 것이다. 그리고 모든 코드에는 나름의 사정이 있다. 그러니 불평하지 말고 개선할 거리를 긍정적으로 찾아보자.

코드 수정 비용을 낮추려면 결국 코드를 수정하기 쉬운 구조로 바꿔야 하고, 그 방법 중 하나가 리팩터링(refactoring)이다.
리팩터링은 새로운 기능을 추가하거나 기존 기능을 개선하지 않는다. 그래서 겉으로 드러나는 이점이 없다. 하지만 리팩터링을 하고 나면 장기적 관점에서 이점이 생긴다. 코드 가독성이 높아지고 리팩터링 이전보다 구현 변경과 확장이 용이해진다. 이러한 변화는 단기적으로는 수정 비용을 낮춰주고 장기적으로는 개발 비용을 줄여준다.

# 미사용 코드 삭제
삭제하기 두렵다면 해당 코드에 TODO 주석을 추가하자.
메서드와 클래스를 삭제할 때는 리플렉션으로 접근하는 코드인지 확인해 봐야 한다.

# 매직 넘버
매직 넘버는 그 값이 무엇을 의미하는지 유추하기 어렵기 때문에 정확하게 코드 의미를 이해하려면 여러 다른 요소를 함께 분석해야 한다.
실제 값은 상수로 정의해서 사용하거나, ENUM 타입을 사용해서 이름에 코드 값을 함께 포함하는 방식으로 변경해보자.
/***************
 * before
 ***************/
if (NumberUtils.anyMatch(boilerType, 19, 20)) {
    return true;
}
 
/***************
 * after
 ***************/
public static final GAS_BOILER = 19;
public static final INDUSTRIAL_BOILER = 20;
if (NumberUtils.anyMatch(boilerType, GAS_BOILDER, INDUSTRIAL_BOILER)) {
    return true;
}

// or
public enum BoilerType {
    GAS_19(19), INDUSTRIAL_BOILER_20(20);
}
if (NumberUtils.anyMatch(boilerType, BoilerType.GAS_19, BoilerType.INDUSTRIAL_BOILER_20)) {
    return true;
}

# 이름 변경
이름 변경은 가장 쉬우면서도 개발 도구가 가장 잘 지원하는 리팩터링이기도 한다.
단어를 고르는 데 어려움이 있다면 시간을 많이 낭비하지 말고 일단 당장 생각나는 단어를 사용해서 구현하자. 대신 주석으로 어떤 의미인지 적어 놓으면 된다. 이후 더 나은 단어가 떠오르면 그때 이름을 바꾸는 리팩터링을 하자.

# 메서드 추출
메서드 추출은 논리적으로 하나의 작업을 수행하는 코드를 대상으로 한다. 추출한 메서드에 알맞은 이름을 부여함으로써 가독성이 좋아지고, 관련 코드가 한 메서드에 모이면서 코드도 더 응집된다.
메서드를 추출하기 좋은 대상은 if-else 의 각 블록에 있는 코드이다. 무턱대고 메서드를 추출하면 안된다. 가독성이나 응집도가 좋아지는 방향으로 메서드를 추출해야 한다. 메서드 추출을 잘못하면 오히려 코드를 탐색하는 데 부담이 증가하고 가독성이 떨어지며 코드를 추적하기가 어려워질 수 있다. 이런 증상이 나타나는 주된 이유는 메서드로 추출한 코드 블록이 개념적으로 구분되는 로직이 아니기 때문이다.

# 클래스 추출
메서드 추출로 코드를 정리했지만 파라미터 전달 관계가 복잡해지는 경우, 메서드 추출 대신 클래스 추출을 고려해 볼 수 있다.
로직을 클래스로 추출하면 해당 로직만 따로 테스트할 수 있는 이점도 생긴다.

# 클래스 분리
한 클래스에 많은 기능이 모여 있으면 각 기능을 별도 클래스로 분리하고, 기능을 분리할 때는 한 번에 다 하기보다는 한 기능씩 점진적으로 진행한다.

# 메서드 분리
서로 다른 기능을 한 메서드에 구현하면 유사한 if-else 가 곳곳에 생겨 코드가 복잡해지고 실행 흐름 추적이 어려워진다. 이 상태에서 메서드 추출 같은 리팩터링을 하면 가독성은 개선되지 않고 구조만 더 복잡해질 수 있다. 메서드가 완전히 구분되는 기능을 구현하고 있는 경우에는 각 기능을 구현하는 메서드를 따로 만들고 분리해서 기능별로 응집도를 높여야 한다.
* 메서드 분리 순서
   1. 두 기능 중 한 기능을 위한 메서드를 추가. 이 메서드는 내부에서 기존 메서드를 호출한다.
   2. 기존 메서드를 호출하는 코드가 새 메서드를 호출하도록 변경한다.
   3. 기존 메서드의 코드를 새 메서드로 이동한다.
   4. 기존 메서드 이름 변경한다.
   5. 코드를 정리한다.

# 파라미터값 정리
메서드에서 사용하지 않는 파라미터 데이터는 코드 분석을 어렵게 만들기 때문에 제거해야 한다. 한 타입을 여러 메서드에서 파라미터로 사용하거나, 같은 파라미터 타입이 메서드 호출 흐름대로 전파될수록 어떤 값을 사용하는지 확인하는 과정이 배로 어려워진다.
문제는 여러 메서드에서 한 타입을 파라미터로 사용할 때 발생한다. 여러 메서드가 한 타입을 파라미터로 사용하면 자연스럽게 특정 메서드에서만 필요로 하는 값이 타입에 추가된다. 나머지 메서드에서는 사용하지 않는 값이 파라미터에 추가되는 것이다.

# for 에서 하는 2가지 일 분리
for 문에서 여러 가지 작업을 실행하면 서로 다른 목적을 가진 코드가 뒤섞일 수 있다. for 루프가 1개의 일만 하도록 논리적이 단위로 분리하면 다음과 같은 이점이 생긴다.
* 코드가 복잡해지지 않고 논리적인 단위로 구분된다.
* 논리적인 단위로 구분되어 코드를 이해하기가 쉽다.
* 메서드 추출과 같은 리팩터링이 용이하다.
* 다른 로직을 추가하기가 용이하다.
루프를 한 번만 돌면 되는데 여러 번 돌게 되면 성능이 느려진다고 걱정하는 개발자도 있다. 하지만 미리 걱정할 필요는 없다. 대부분 성능에 문제가 없다. 정말로 문제가 될 때만 측정해서 개선하면 된다. 그리고 복잡한 코드보다 이해하기 좋은 코드가 주는 이점이 훨씬 크다.

 모든 코드에는 나름의 사정이 있다. 그리고 그 사정은 그때는 맞고 지금은 다를 수도 있다.

그 코드의 과거를 들추는건 비난을 위해서가 아닌, 앞으로의 개선을 위해서 임을 잊지말자.

그러니 왜 그렇게 되어 있냐만 물어보고 끝내지말고 개선안에 대한 피드백 한마디라도 해달라.


테스트

# 자동화된 테스트·회귀 테스트·안정감
테스트를 수작업으로 진행하면 테스트를 아예 하지 않는 것보다는 낫지만 아무래도 다양한 경우의 수를 확인하기 어렵다. 현실적으로 모든 경우의 수를 테스트할 수는 없기 때문에 버그를 놓칠 수 있다. 운이 좋아 버그가 빨리 발견되어 바로 대응할 수도 있고 버그가 한참 뒤에 발견되어 원인을 분석하는 데 어려움을 겪기도 한다.
개발자는 항상 압박 속에 산다. 테스크 코드가 검증하는 범위가 넓어질수록 내가 만든 코드가 문제를 일으키지 않는다는 확신이 커지고 코드를 수정할 때 안정감을 느끼게 된다. 코드 변경에 대한 스트레스도 감소한다.

# 테스트 커버리지에 직찹하지 말기
테스트 커버리지가 높다고 해서 완벽하게 모든 문제를 없앨 수는 없다. 하지만 적어도 테스트를 통과한 코드는 문제가 없다는 것을 확신할 수 있다. 그래서 테스트가 검증하는 범위, 즉 테스트 커버리지(coverage)가 중요하다.
테스트 커버리지는 70~80% 수준이면 적당하다. 90% 이상을 목표로 하면 만들지 않아도 될 테스트 코드를 작성하는 일이 벌어지기 때문이다. 이 경우 테스트 커버리지를 높일 수는 있지만 실제로 테스트 코드를 작성해서 얻을 수 있는 가치가 없다. 가치 없는 코드를 만드느라 시간 낭비하지 말자.

# 테스트 주도 개발과 회귀 테스트
TDD를 진행할 때 예외적인 상황에서의 테스트를 먼저 작성하고 그다음 정상적인 상황에서의 테스트를 작성하는게 좋다. 많은 기능은 정상적인 상황뿐 아니라 예외적인 상황에 대한 대처가 필요한데 TDD는 테스트 코드를 작성할 때부터 이 점을 고려한다.

# 테스트 주도 개발과 설계
TDD가 기능을 설계하는 데 도움을 주기도 한다. 테스트 코드에서 테스트할 대상의 기능을 실행하려면 다음과 같은 것들(클래스 타입·이름, 메서드 이름, 메서드 파라미터 타입, 리턴 타입, 익셉션 타입, 의존 대상·역할)을 정해야 하는데 이것은 기능을 설계하는 과정과 같다. 클래스·메서드가 제공하는 기능이 이름에 잘 표현되어야 하는데 테스트 코드를 작성하려면 바로 클래스·메서드 이름부터 결정해야 한다.

# 테스트 주도 개발과 생산성
상황에 따라 변경하려고 하는 코드는 간단하지만 테스트 코드를 작성하는데 더 많은 노력이 들기도 한다. 테스트 코드를 작성하면 처음에는 개발 시간이 늘어나는 것처럼 느껴지지만 시간이 갈수록 반복되는 테스트 시간을 줄여줘서 오히려 개발 시간이 줄어든다는 것을 알 수 있다. 즉 미래의 디버깅 시간과 코딩 시간을 줄여준다.

# 테스트 가능성
테스트를 먼저 만들던, 나중에 만들던 중요한 것은 테스트 가능성(testability)을 높이는 데 있다.
코드를 만들 때 테스트 가능성을 염두에 두면 개발 생산성과 설계 품질을 높일 수 있다.

# 리팩터링을 위한 테스트 작성하기
리팩터링을 할 때는 리팩터링 전과 후의 코드가 동일하게 동작하는지 보장할 수 있어야 한다. 그러기 위해 리팩터링 전에 테스트 코드를 먼저 작성해야 한다. 기존 코드에 테스트 코드를 먼저 만들고 그 다음 리팩터링을 진행하면 리팩터링 후에 코드가 동일하게 동작하는지 검증할 수 있다. 
기존 코드를 검증하는 테스트 코드를 작성하기 쉽지 않은 경우, 일부 코드만 분리하고 테스트를 작성하면서 리팩터링 해보자.

 

아키텍처

# 아키텍처를 결정하는 요인
아키텍처는 그냥 결정하는 것이 아니다. 요즘 특정 아키텍처가 유행한다고 선택해서는 안 된다. 아키텍처를 결정할 때는 크게 2가지를 고려해야 한다. 하나는 기능 요구 사항이고 다른 하나는 품질 속성 또는 비기능 요구 사항이다. 

# 트레이드 오프
품질 속성을 높이면 시스템의 복잡도와 비용이 증가하고 서로 상충하는 품질 속성도 존재하기 때문에 아키텍처를 선택할 때 높이고자 하는 품질 속성 간의 절충이 필요하다. 절충하는 과정에서 하나를 얻으면 하나를 잃게 된다. 감당할 수 있는 수준의 복잡도, 사용자 규모 대비 적절한 인프라 비용, 시스템에 기대하는 최소 품질을 고려해서 아키텍처를 결정해야 한다. 아키텍처를 설계할 때 모든 면에서 최고인 아키텍처를 추구하면 안 된다. 모든 게 완벽한 아키텍처가 아닌 가장 나쁘지 않은 아키텍처를 선택해야 한다.

# 주요 아키텍처 품질 속성
* 가용성(availability) : 시스템이 얼마나 오랫동안 사용할 수 있는지를 나타내는 속성. 99.99%와 같이 사용 가능 시간/전체 시간 비율로 표시.
* 성능(performance) : 시스템의 최대 처리량, 평균 응답 시간 등을 포함하는 속성.
* 확장성(scalability) : 자원을 추가해서 증가한 사용자나 트래픽을 처리할 수 있는 시스템의 속성.
* 탄력성(elasticity) : 필요에 따라 자원을 추가하거나 반환하는 능력.
* 견고성(robustness) : 실행 중에 발생하는 에러나 잘못된 입력을 다루는 능력.
* 결함 허용(fault tolerance) : 일부 기능에 장애가 발생해도 시스템이 운영을 지속할 수 있는 능력.
* 신뢰성/안정성(reliability/safety) : 시스템 고장에 대비한 안전장치가 필요한지 또는 생명에 영향을 주는 중요한 시스템인지를 나타내는 속성.
* 유지보수성(maintainability) : 얼마나 쉽게 시스템을 변경하고 향상할 수 있는지를 나타내는 속성.
* 지역화(localization) : 다양한 언어에 대한 지원을 표현하는 속성.
* 테스트 가능성(testability) : 소프트웨어 결과물이 주어진 테스트 환경에서 얼마나 테스트할 수 있는지를 나타냄.
* 합법성(legal) : 시스템이 지켜야 할 법적 규제나 요건.
* 보안(security) : 데이터베이스에 암호화해서 저장해야 할 데이터, 통신 구간의 암호화 등을 나타내는 속성.
* 배포 가능성(deployability) : 개발한 결과물을 제품에 쉽게 반영할 수 있는 정도를 표현.
* 추적성(traceability) : 무언가를 추적할 수 있는 능력.

# 아키텍처가 중요한 이유
시스템이 커질수록 전체 시스템 설계가 개별 구현보다 중요해진다.
* 아키텍처는 시스템의 골격 역할을 한다.
   만들려는 시스템에 따라 적합한 아키텍처가 존재한다.
* 아키텍처는 품질 속성에 영향을 미친다.
   선택한 아키텍처에 따라 높일 수 있는 품질 속성이 있고 높이기 어려운 품질 속성이 있다. MSA 를 선택하면 탄력성, 배포 가능성(독립적 배포)이 커지지만 데이터 무결성을 위한 구조는 더 복잡해진다.
* 아키텍처는 기능과 직교한다.
   API 서버를 비동기 프레임워크로 구현하면 일부 성능 지표를 높일 수 있지만, 관리자를 위한 백오피스 기능이나 CPU 연산이 많은 작업에는 적합하지 않다.
* 아키텍처는 시스템을 제한한다.
   인증·인가를 위한 선택한 구조에 따라 관리할 수 있는 인가 범위가 달라지거나, 가비지 컬렉터를 사용하는 시스템은 가비지 컬렉터가 동작하면서 발생하는 지연 시간을 막을 수 없다. 필수로 사용해야 하는 외부 라이브러리가 특정 버전의 프레임워크에서만 동작할 수도 있다.

# 아키텍처 변경
시간이 흐르면서 요구되는 품질 속성이 달라졌으므로 그것에 맞게 아키텍처도 변해야 한다.
아키텍처 변경은 흥미롭지만 단순히 재미 삼아 할 일이 아니다. 또한 요즘 유행하는 방식 또는 최근에 학습한 내용을 적용하고 싶다고 해서 아키텍처를 변경하면 안 된다. 아키텍처 변경은 반드시 필요성에 기반해 이루어져야 한다. 요구되는 품질 속성의 변화가 없다면 아키텍처를 함부로 변경하면 안 된다. 아키텍처 변경에는 많은 시간과 노력이 따르기 때문에 신중하게 고려해야 한다.
아키텍처를 변경할 때는 확실하게 얻는 이점과 소요될 개발 비용을 고려해야 한다.

# 본질적 복잡성과 우발적 복잡성
해결해야 할 문제와 상관없는 복잡함을 우발적 복잡성(accidental complexity)이라고 부른다. 
반대로 해결해야 할 문제 자체가 복잡해서 생기는 복잡함을 본질적 복잡성(essential complexity)이라고 한다. 
개발자는 우발적 복잡성에 빠지지 않도록 경계해야 한다. 물론, 흥미로운 기술을 만나면 사용해 보고 싶고, 유명한 회사에서 썼다는 기술도 써 보고 싶기 마련이다. 심할 때는 어떤 기술을 쓰지 못하게 막는 상사한테 화가 날 때도 있다. 하지만 그럴수록 우발적 복잡성의 유혹에 빠지지는 않았는지 곱씹어보자. 당장 마주한 문제 또는 곧 닥칠 문제를 해결하기 위해 어쩔 수 없이 복잡성이 동반되어야 한다면 지속해서 설득하는 시간을 가져야 한다. 같은 문제를 해결하려 한다면 복잡한 구조보다 단순한 구조가 더 좋다는 사실을 항상 명심하자.

# 패턴 익히기
특정 맥락에서 반복되는 문제 해결법을 패턴(pattern)이라고 부른다. 상황에 맞는 패턴을 사용하면 설계 시간을 단축할 수 있기에, 여러 가지 설계 패턴을 알고 있으면 설계 품질과 유지보수성을 높이는 데 도움이 된다.
* 아키텍처 패턴
   아키텍처 수준에서의 패턴. 예를 들어 이벤트 기반 아키텍처를 사용하면 탄력성과 성능에는 장점이 있지만 트랜잭션 처리가 복잡해지고 테스트도 어려워진다. 요구하는 성능이 낮거나 규모가 작다면 계층 아키텍처를 기반으로 한 모놀리식 구조를 사용하는 게 나을 수 있다.
* 디자인 패턴
   유명한 GoF의 디자인 패턴
* 기업 통합 패턴
    시스템간 통합을 위한 패턴
* 결함 허용 패턴
   에러 처리에 충분히 신경 쓰면 오류를 줄일 수 있지만 완전히 장애를 없앨수는 없다. 제아무리 완벽하게 구현해도 어딘가에 구멍은 있기 마련이고, 설사 내가 만든 시스템이 완벽하더라도 연동하는 다른 시스템에 문제가 생길 때도 있다. 따라서 문제를 완전히 없애기보다는 문제가 생겼을 때 알맞게 대처하는 방법을 찾아야 하는데 이때 사용할 수 있는 패턴이 결합 허용(fault tolerance)패턴이다. 결함 허용 패턴은 에러 발견, 에러 복구, 에러 완화 등 어떻게 처리할지에 대한 패턴을 포함하는 개념이다. Heartbeat, Restart, limit Retires, Circuit Breaker 등이 결함 허용과 관련된 패턴이다.

# 패턴이 유용한 이유
패턴은 2가지 측면에서 유용하다.
첫 번째는 설계 시간을 단축해준다. 패턴은 맥락을 포함한다. 어떤 상황일 때 이런 패턴을 사용하라는 식이다.
두 번째는 원활하게 소통할 수 있게 해준다. 패턴은 이름을 갖고 있다. 상황, 구조, 동작 방식 등을 구구절절 설명할 필요 없이 이름만 말하면 모든 정보가 전달된다. 짧은 이름만으로도 다양한 정보가 전달되니 소통 효율이 높아진다.

 

업무관리

# 처음부터 끝까지
하나의 일을 주면 처음부터 끝까지 책임지는 것이 기본. 개발자는 요구 사항 분석부터 구현, 출시까지 이어지는 일련의 과정을 관리하고 마무리해야 한다. 여기서 책임은 혼자서 모든 일을 다 해야 한다는 뜻이 아니다. 일이 진행될 수 있게 관리해야 한다는 뜻이다.

# 업무 나누기
개발 규모가 커졌을 때 흔히 하는 실수 중 하나가 생각나는 대로 개발하는 것이다. 작은 일을 맡아서 할 때는 체계적으로 진행하지 않아도 일을 완료할 수 있다. 일을 진행하기 위한 정리 작업은 필요하지만 생각나는 대로 해도 잘못될 가능성이 크지 않다.
반면 일의 규모가 커지면 그냥 생각나는 대로 하면 안 된다. 즉흥적으로 일을 하면 제대로 끝내지 못할 가능성이 높다. 업무의 크기에 따라 일하는 방식을 배워야 한다.

일은 하루에 수일 이내에 끝낼 수 있는 크기로 나눈다.
* 요구 사항 분석, 기능 전반 이해 → 기획자 리뷰,현업 리뷰
* 개발 계획 → 규모 파악, 계획 초안
* 설계 초안
* 개발
* QA

일의 규모가 커지면 개발 계획도 세워야 한다. 어차피 예정대로 일이 진행되지 않으니 굳이 개발 계획을 짜는 데 시간을 들일 필요가 없다고 생각할 수도 있따. 하지만 일의 규모가 커지면 개발 계획은 반드시 세워야 한다. 물론 계획은 말 그대로 계획이므로 완벽하게 계획한 일정에 맞춰 일을 진행하기는 어렵지만, 계획이 있어야 진행 상태를 파악하고 변화에 대응하며 조정할 수 있다.
계획을 세우려면 일의 규모를 파악해야 한다. 규모를 파악하려면 해야 할 작업 목록이 필요하다. 작업 목록에 넣어야 하는 항목 중 하나가 개발할 기능 목록이다. 기능 목록은 시스템 사용자 입장에서 구분되는 단위로 작성하면 좋다. 명확하게 구분되는 기능 단위로 작업 목록에 추가한다. 모호한 표현을 사용하면 안된다. 기능 목록 외에 개발 자체에 대한 업무도 작업 목록에 넣어야 한다. 작업 목록을 작성했다면 작업마다 대략 얼마나 시간이 걸릴지(일단위 또는 주단위) 추정해본다. 어느 정도 규모인지 파악할 수 있는 수치를 도출할 수 있다는 것에 중요한 의미가 있다.

# 위험 관리
본인이 느끼기에 뭔가 잘 진행되지 않거나 모르는게 있을 때 또는 명확하지 않은 점이 생겼다면 위험 신호라고 여겨야 한다. 위험 신호를 감지하면 빠르게 공유해야 한다. 어떻게 개발해야 할지 감을 못 잡고 있으면서 어떡해서든 혼자 해보겠다고 발버둥 치면 안 된다. 오히려 문제를 더 키울 뿐이다. 따라서 스스로 위험 신호를 감지하면 즉시 공유해야 한다. 관리자에게 위험에 대비할 수 있는 시간을 주지 않으면, 위험을 피하고자 무리수를 두게 되고 자칫 잘못하면 조직에서 신뢰를 잃을 수도 있다.
위험 상황을 관리하기 위해 미리 위험 목록을 작성해보자. 위험 요소는 어떤게 있는지 검토해보고 5개 이상 찾아서 목록을 만들어보자.
위험 목록을 만들 때 등급을 함께 정리하면 더 좋다. 등급이 높을 수록 일에 주는 영향이 크니 신경 써서 관리해야 한다.

# 요구사항은 바뀐다.
이런저런 이유로 요구사항은 바뀌게 되어 있다.
요구 사항이 바뀌다는 사실을 인정하고 요구 사항을 대하는 방식을 바꿔야 한다. 초반에 요구 사항을 고정하기 위해 많은 노력을 기울이기보다는 요구 사항의 변동 폭을 줄이는 데 초점을 맞춰야 한다. 요구 사항의 변동 폭을 줄이려면 왜 이런 요구 사항을 원하는지 이해하려는 노력이 필요하다. 요구 사항은 주로 개발자가 아닌 비개발자가 요구한다. 비개발자는 본인이 이해하는 수준에서 요구 사항을 말한다. 이런 요구 사항은 현재 시스템의 한계로 구현이 어려울 수 있다. 또는 반대로 요구 사항이 논리적으로 이해 안 될 때도 있다. 이럴 때 개발자는 그저 해달라는 대로 해주면 안 된다. 왜 그런 요구를 했는지 이유를 들어봐야 한다. 특정 기능을 요구한 이유를 듣다보면 더 나은 방식이나 다른 대안이 떠오르기도 한다.
요구 사항의 변동 폭을 줄이는 또 다른 방법은 요구 사항을 나워서 분석하는 것이다. 초반에 모든 요구 사항을 세세하게 분석해서 협의하지 말고 개략적으로 분석한다. 전체 요구사항 중 절반가량을 초반에 집중해서 개발하고, 개발이 30~40% 정도 진행된 다음에 나머지 요구 사항을 정리하는 식이다.
오픈 시점에 포함하지 않아도 되는 기능은 우선순위를 조정해서 오픈 이후에 개발하는 형태로 이견을 조율할 수 있다. 보통 어떤 기능을 넣기 위해 오픈 시점을 미루는 것보다 빠르게 오픈하는 게 더 중요하기 때문이다.

결국 우선순위 문제다. 모든 요구 사항이 중요하다고 하지만 일정과 비용을 맞닥뜨리면 얘기가 달라진다. 구현 비용이 커지거나 원하는 일정 내에 구현이 어려워진다면 우선순위가 낮아지는 요구 사항이 생긴다. 관계자가 반드시 넣어야 한다고 말했던 기능이 꼭 구현하지 않아도 되는 기능으로 바뀌거나 요구 조건의 복잡도가 단순해지기도 한다. 일정과 비용은 개발 범위를 결정할 때 강력한 기준이 될 수 있다.
개발자는 소프트웨어를 만들어 요구 사항을 해결해주는 사람이다. 당연히 요구를 최대한 들어주기 위해 노력해야 한다. 하지만 그렇다고 모든 요구 사항을 수용하고 해결해주는 사람은 아니다. 처음부터 일정과 비용 얘기를 꺼내지는 말자. 일단 왜 그런 요구를 하는지 들어보자. 그리고 대안이나 우선순위에 대해 논의하자. 이런 과정을 거쳤음에도 요구 사항이 협의가 안된다면 그때 일정과 비용을 언급하자.

# 점진적·반복적 개발
점진적 개발(incremental development)은 결과물을 구분되는 조각으로 나누고 각 조각을 점진적으로 완성하는 방식이다.
점진적 개발의 핵심은 작업을 분할해서 더 빨리 가치를 제공한다는 데 있다. 점진적으로 사용자가 요구하는 기능을 제공함으로써 빠르게 사용자 만족도를 높일 수 있고 사용자 피드백을 신속히 얻을 수 있다.
반복적 개발(iterative development)은 사용자 요구 사항 또는 제품 일부분을 반복해서 개발하여 목표로 하는 결과를 만드는 방식이다.
난이도가 높은 개발을 진행할 때 주료 사용하며, 연속된 활동으로 원하는 목표에 도달하는 방식이 반복적 개발이다.
덩어리가 큰일이 있다면 관리할 수 있는 단위로 작게 나눌 뿐만 아니라 점진적이고 반복적으로 개발할 수 있도록 계획을 세워야 한다. 한 번에 모든 일을 다 진행하기보다는 조기에 가치를 제공할 수 있도록 점진적으로 기능을 출시하고, 난이도가 높은 일은 반복적으로 완성해서 위험을 낮출 수 있다.

# 안 된다고 말하기·대안 제시하기
개발자는 "안 된다"라고 말할 수 있어야 한다. 일단 약속하면 지키기 위해서 노력해야 하지만 노력만으로 할 수 없는 일은 분명히 못 한다고 해야 한다. 지키지 못할 약속을 하면 신뢰가 깎일 뿐 아니라 같이 일하는 사람도 힘들어진다. 안 된다고 말할 때는 할 수 없는 이유도 함께 전달해야 한다.
어려운 요구가 들어온다면 안 된다고만 하지 말고 대안을 찾아보자. 요구하는 이유가 무엇인지 들어보고 함께 고민하다 보면 해결할 다른 방안이 떠오를 때가 많다. 요구를 있는 그대로만 생각해서 못 한다고 하기보다 요구를 충족할 수 있는 대안을 찾아 제공하는 게 더 가치 있다고 생각한다. 또한 대안을 찾기 위해 함께 협업하는 과정에서 동료 간 신뢰도 높일 수 있다. 이후에는 협의가 더 부드럽게 진행된다.

# 수작업 줄이기
주기적으로 반복해서 해야 하는 수작업이 있다면 자동화해야 한다.

# 이유와 목적 생각하기
경력이 쌓이고 지위가 올라가면 단순히 시키는 일만 하면 안 된다. 반대로 누군가에게 일을 맡길 때도 단순히 어떻게 하라고 지시만 하면 안 된다. 일에는 이유와 목적이 있기 때문이다. 상급자로부터 업무 지시를 받으면 어떤 이유 또는 어떤 목적으로 그 일을 줬는지 알아내야 한다. 단지 결과만 만들면 되는 게 아니다. 이유와 목적은 올바른 결과를 만들었는지 판단할 수 있는 기준이 된다. 이유와 목적을 모른 채 어떻게 일을 할지만 고민하면 엉뚱한 결과를 만들게 된다.

개인적으로 일하는 방법은 주니어때부터 좋은 사수에게 잘 배워야 한다고 생각한다.

머리도 좋고 개발도 잘 하지만, 일 하는 방법의 센스가 없어 고생시키는 사람들이 의외로 많기도 하고 이 경우 안타깝기도 하다.

 

정리하고 공유하기

# 글로 정리해서 공유하기
좋은 글을 쓰는 건 어렵지만 연습으로 나쁘지 않은 글, 잘 읽히는 글을 쓸 수는 있다.
글을 잘 못 쓰는 개발자는 일단 글부터 쓰려고 한다. 짧지 않은 글을 쓸 때는 일단 글에 담을 내용부터 정리해야 한다. 글의 주제와 전달하고 싶은 내용이 무엇인지 그리고 어떤 목적으로 글을 쓰는지를 생각해야 한다. 글을 읽을 대상도 중요하다. 
글의 주제, 개요, 목적, 대상을 결정했다면 내용을 어떤 순서로 풀어갈지 고민하자. 내용희 흐름은 자연스럽게 목차로 연결된다. 내용 흐름을 고민하면 자연스럽게 목차가 도출되고, 목차를 잡고 나면 그 안에 담을 내용을 고민한다.

# SCQA 프레임워크로 시작하기
설득을 위한 자료를 작성한다면 SCAQ 프레임워크를 활용하자.
* Situation(상황) : 현재 상태와 문맥을 제공한다.
* Complication(문제점) : 현 상황에서 무엇이 문제 되는지 기술한다.
* Question(의문점) : 문제점에서 도출되는 의문 사항이다.
* Answer(해결) : 의문점에 대한 해결책을 제시한다.

# 정확하게 전달하려고 노력하기
글로 정보를 전달할 때는 내용을 정확하게 표현하려는 노력이 필요하다. 먼저 모호한 표현이나 애매한 단어 사용을 줄이도록 노력해야 한다.
또한 정보를 정확하게 전달하려면 문서를 이해하는 데 필요한 정보를 함께 제공해야 한다. 글이 길어지거나 글만으로 부족하다면 적절한 그림이나 표를 제공해서 보완해야 한다. 시간이 걸리더라도 정리가 잘 된 문서를 만들면 문서를 읽는 많은 사람의 시간을 아낄 수 있다. 읽는 사람이 내용을 파악하는 데 들이는 노력을 줄여주는 것만으로도 문서 작성에 들어간 노력보다 몇 배 이상의 정보 전달 효과를 얻을 수 있다. 마치 코드와 같다. 하나 더, 비개발자를 대상으로 할 때는 개발 용어 사용을 최대한 아끼자.

# 5가지 글쓰기 팁
우리는 정보 전달이 목적인 글을 쓴다.
* 문장 짧게 쓰기 : 문장이 길어진다 싶으면 문장을 둘 또는 그 이상으로 나눠야 한다.
* 글머리 기호 목록·번호 목록 사용하기 : 여러 내용을 나열해야 할 때는 글머리 기호 목록이나 번호 목록을 활용하면 유용하다.
* 표나 그래프 사용하기
* 그림 사용하기
* 시각적 효과 사용하기 : 특정 단어나 문장을 강조해서 더 효과적으로 전달할 수 있다.

# 시간을 내서 글쓰기 연습하기
1주일에 한두 번이라도 글 쓰는 시간을 가져보자. 일단 쓰는 게 중요하다. 자꾸 써야 실력이 는다.

# 발표하기
발표의 핵심은 내용 전달이다. 발표 자료를 만들 때는 먼저 내용에 집중하자. 내용 초안을 만들고 말로 발표 연습을 충분히 하자. 자료를 포장하느라 말하는 연습을 놓치면 안 된다. 내요이 완성되면 그 때 발표 자료에 금칠을 해도 늦지 않다. 
청중을 웃기려는 목적으로 개발자 짤 또는 밈이라 불리는 그림이나 유머를 무리해서 사용할 필요는 없다. 내가 먼저 발표 내용과 이야기를 전개하는 데 집중하면 청중도 내 발표에 집중한다. 물론 모든 청중을 집중시킬 수는 없지만 내 이야기에 관심을 가진 청중은 자연스레 발표에 집중하게 된다.

# 외래어 남용하지 않기
발표는 내가 아니라 듣는 사람을 위해서 하는 것이다.

 의외로 고연차분들중에서 정리하기, 발표하기 등 정보 전달이 미숙한 분들이 있다.

이 부분은 스스로 노력도 필요하지만, 주위 여건이 발표를 많이 할 수 밖에 없는 환경등으로 주어져야 하는 요인도 못지않게 필요하다.

나 같은 경우에, 처음 외부고객 지원 엔지니어로 일하면서 다양한 사람들과 소통해야 했던 점과

회사에서 많은 발표 기회를 통해 꾸준히 연습할 수 있었던 점이 많은 영향을 끼쳤던것 같다.

그런 면에서 주니어에서 중니어로 가는 과정에서 이 부분의 능력을 잘 키워놓으면,

시니어를 넘어서까지 오랫동안 유용한 능력중 하나가 될 수 있다고 확신한다.

 

리더

# 리더 연습하기
서로에게 주는 영향력을 생각해봤을 때 우리 모두 리더가 될 수 있다. 좋든 싫든, 의식하든 의식하지 않든 간에 우리는 주변에 영향을 주기 때문이다. 리더십이 반드시 상사와 부하 직원 사이에서만 형성되는 게 아니다. 주변에 영향을 주고 있다면 그게 바로 리더십이다. 타고난 리더십을 가진 사람도 있다지만 대부분은 성장하는 과정에서 리더십을 배운다. 탁월한 리더는 아무나 될 수 없겠지만 리더십을 향상할 수는 있다. 리더십을 향상하는 데 경험이 정말 중요하지만, 경험만으로는 부족하다. 경험은 제한된 상황에서 이뤄지기 때문에 경험하지 못하는 상황이 훨씬 많다. 경험에서 부족한 부분을 책과 강의로 채워 나가야 한다.

# 문제 해결 리더십
개발자는 기술로 문제를 해결한다. 이런 관점에서 보면 개발자에게 필요한 리더십이 문제 해결 리더십인 것은 당연해 보인다.
뛰어나 기술 리더는 혁신, 즉 '더 좋은 방법으로 무언가를 한다'는 가치로 사람들의 능력을 발휘하게 하고 혁시을 위해 다음 3가지 활동에 힘들 준다.
* 문제 이해하기 : 문제를 올바르게 해결하려면 당연히 어떤 문제인지 제대로 이해해야 한다.
* 아이디어 흐름 관리하기 : 문제를 해결할 아이디어도 관리해야 한다. 내가 제시한 아이디어가 최고라는 자만에 빠지지 않도록 주의해야 한다. 다양한 의견을 청취하고 수용하려는 노력이 필요하다.
* 품질 유지하기 : 문제를 해결하는 뛰어난 아이디어가 있다고 하더라도 결과물의 품질이 떨어지면 아무 소용이 없다. 문제 해결 리더는 품질을 일정 수준 이상으로 유지하기 위해 노력해야 한다.

# 사람이 아닌 프로세스·시스템 변화시키기
사람은 쉽게 바뀌지 않는다. 변화는 결국 본인 스스로 해야 한다. 다른 사람이 변화할 때 내가 촉매제가 될 수는 있지만 본인의 의지가 없다면 변화는 일어나지 않는다. 그러니 사람을 변화시키려고 애쓰지는 말자. 변화가 필요하다면 사람이 아닌 프로세스와 시스템에 집중하자. 프로세스와 시스템을 바꾸고 사람들이 그 프로세스와 시스템을 따르도록 만들자. 이런 과정에서 변화가 자연스럽게 생긴다. 프로세스와 시스템 변경이 쉽다는 뜻이 아니다. 사람을 변화시키는 것보다는 조금이나마 수월하다는 의미다.

# 기술력 상실의 두려움 없애기
넓은 시야와 깊은 수준으로 시스템을 바라보고 아키텍처를 설계하는 역량은 구현 못지 않게 중요한 기술 역량이다. 복잡한 시스템을 알맞은 단위로 분해하고 진행 계획을 세우는 역량 역시 시니어 개발자가 가져야 할 중요 역량이다. 동료가 제시한 구현 기술 후보 중에서 현재 상황에 맞는 기술을 선택할 수 있는 기준을 갖추는 것도 중요한다. 자의든 타의든 리더나 관리자 역할을 맡게 될 때 그동안 쌓아 올린 기술력을 상싱하게 된다는 두려움을 갖지 말자. 대신 고참 개발자로 성장하는 데 필요한 여러 역량을 골고루 높일 기회로 생각하자.

# 대신하지 않기
직원을 돕겠다는 마음으로 직원이 할 일을 리더가 대신하기도 한다. 직원이 맡은 기능을 구현하는 식으로 말이다. 내가 가진 역량을 발휘해서 직원을 도왔다는 생각에 뿌듯할 수도 있다. 하지만 직원을 도왔다고 볼 수 없다. 오히려 직원이 성장할 기회를 훔친 것이다.
직원의 성장을 바란다면 일을 대신하지 말고 마음의 여유를 갖자. 위기 순간에는 빠른 조치를 위해 직접 나서야 할 때도 있지만 위험한 상황이 아니면 대신 하고 싶다는 유혹을 견뎌야 한다. 돕고 싶다면 구현 안을 함께 검토하거나 짝코딩을 사용해서 지식을 전파하는 식의 다른 형태로 지원하자.

# 자율성
일을 맡겨 놓고 작은 것까지 하나하나 지시하는 마이크로 매니저는 직원의 자율성을 뺏는다. 자율성이 없는 직원은 주도성을 잃는다. 직원에게 동기 부여를 해주고 주도적으로 참여하도록 유도하고 싶다면 자율성을 최대한 보장해줘야 한다. 간섭할수록 주도성은 떨어진다. 일단 맡겼다면 간섭을 최소화하고 기다리자.
자율성에도 어느 정도의 통제가 필요하다. 통제는 하나하나 작은 것까지 지시하는 관리가 아니고, 진척 상황을 확인하고 위험 요소를 검토한 다음 샛길로 빠지지 않게 막아주는 역할을 의미한다.

# 도움 요청하기
리더가 가져야 할 책임은 일을 제대로 끝내는 것이다. 그저 열심히만 해서는 안 된다. 힘든 일이 있거나 도움이 필요하면 상위 직급자한테 지원 요청을 하거나 함께하는 직원에게 도움을 구하자. 제때 도움을 구하지 않아 일이 엉망이 되는 것 보다 제때 도움을 구해 일이 제대로 진행되는 것이 낫다.

# 규모의 비경제 이해하기
프로젝트가 지연되면 개발자를 추가로 투입하는 것을 검토하는 리더가 많다. 인력을 더 투입하면 개발이 더 빨리 끝날 것 같은 느낌이 들기 때문이다. 하지만 잘못하면 정반대의 결과가 벌어진다. 소프트웨어 프로젝트는 규모가 커질수록 경제성이 떨어질 가능성이 높다.
지연된 프로젝트에 개발자를 추가로 투입하면 일정이 더 늦어진다는 것이다. 이것을 브룩스의 법칙(Brook's low)이라고도 한다. 이 법칙에 따르면 진행 중인 프로젝트에 인력을 추가로 투입하면 소통 비용과 부하가 늘어나면서 개발 시간이 줄기는커녕 오히려 증가한다.
소프트웨어 개발에서 규모의 비경제를 이겨내는 방법은 대규모 프로젝트를 여러 개의 작은 프로젝트로 나누는 것이다. 프로젝트를 나누고 각 프로젝트에 소규모 팀을 할당하면 개발자 생산성도 높아지고 프로젝트 성공 가능성도 올라간다.
프로젝트를 나눌 때는 독립성을 중점에 둬야 한다. 각 프로젝트가 최대한 독립적으로 진행되어야 규모에서 오는 비경제성이 줄어든다. 결합도를 낮추는 방식으로 아키텍처를 구성하면 독립적으로 프로젝트를 진행하는데 도움이 된다.

 

팔로워

리더십만큼이나 팔로워십도 중요하다. 팔로워십은 단순히 리더를 따르는 것을 의미하지 않는다. 팔로워십은 리더와 조화를 이루고 능동적으로 일을 수행하면서 리더가 성공할 수 있도록 지원하는 것을 말한다. 팔로워가 없다면 리더는 아무것도 할 수 없다. 반대로 리더가 조직 내에서 성과를 내지 못하면 팔로워 역시 성과를 내기 어렵다. 리더와 팔로워는 공생 관계이다. 팔로워는 리더와 소통하고 공감하며 문제를 발견하고 의견을 제시해서 리더와 함께 조직의 목표를 달성하는 데 기여한다.

# 팔로워십과 영향력
리더 역시 사람이고 잘못된 결정을 하기 마련이다. 이때 팔로워십이 필요하다. 좋은 팔로워는 리더가 제시하는 방향을 잘 지원하고 따르는 것뿐 아니라 리더가 잘못된 의사 결정을 내렸을 때 리더가 올바른 방향으로 이끌어갈 수 있도록 노력한다. 즉 팔로워는 리더에게 영향력을 행사한다. 리더가 불합리해 보이는 의사 결정을 내린 이유는 조직적인 이유 때문일 수도 있다. 또는 내가 미처 생각하지 못한 문제 때문일 수도 있다. 그러니 리더가 왜 그런 결정을 했는지 맥락을 파악해야 한다.
팔로워십을 발휘하려면 상향 관리가 중요하다. 관리자와 관계를 유지하는 방법으로 다음 3가지를 제시한다.
* 관리자를 놀라게 하지 말자. 관리자를 놀라게 하면 신뢰가 사라질 수 있다.
* 관리자에게 놀라지 말자. 관리자가 모든 세세한 사항을 챙길 것이라고 기대하지 말자. 대신 관리자와 적극적으로 소통해서 정보·피드백을 얻자.
* 관리자에게 관련 정보를 제공하자. 유용하다고 생각하는 정보가 있다면 관리자에게 전달한다.

# 나쁜 팔로워 되지 않기
나쁜 팔로워에 대한 정의
* 아무것도 하지 않기(전혀 관여하지 않기)
* 나쁜 리더(비효율적이거나 비도덕적인)를 지지하기
* 좋은 리더(효율적이고 도덕적인)를 반대하기
나쁜 리더를 막을 만큼 영향력을 행사하지 못하더라도 나쁜 리더의 방향을 지지하지 않는 방법으로 나쁜 팔로워가 되지 않을 수는 있다. 나쁜 리더가 있다고 해서 나쁜 팔로워가 되지는 말자.

# 이끌거나 따르거나 비켜서라
여러 사람과 함께 일한다면 둘 중 하나는 해야 한다. 리더가 되어 누군가를 이끌거나, 팔로워가 되어 누군가를 따라야 한다. 이도 저도 싫다면 그들이 나아갈 수 있게 비켜서야 한다. 경력이 쌓이면 누구나 팔로워이면서 동시에 리더가 된다. 팀장을 따르면서 나보다 경험이 부족한 동료를 이끌어야 한다. 이때 좋은 리더가 되려면 먼저 좋은 팔로워가 되어야 한다. 좋은 팔로워는 리더가 의사 결정하는 과정에 참여하고 좋은 결정을 내릴 수 있게 함께 고민한다. 이런 경험이 좋은 리더가 되는 씨앗이 된다.

# 겸손·존중·신뢰
리더와 팔로워, 동료는 한 팀으로 협업해야 한다.
함께 일하고 싶은 리더, 팔로워, 동료가 되려면 겸손이 필요하다.
겸손·존중·신뢰 중에 가장 가지기 힘든 것이 신뢰다. 신뢰를 주는 것과 동시에 신뢰받기 위해 노력해야 한다. 신뢰는 역량과 성품을 기반으로 이루어진다고 했다. 즉 태도가 좋아도 역량이 없으면 신뢰가 생기지 않는다. 반대로 역량이 좋아도 태도가 나쁘면 신뢰하기 어렵다. 신뢰를 만들기 위해서는 좋은 태도를 유지하면서 역량을 높이는 노력이 필요하다. 한 번 맺은 관계는 프로젝트보다 더 오래 간다. 그러니 관계의 힘을 무시하지 말자. 지금 얻은 신뢰가 앞으로 큰 도움이 될 것이다.

다양한 역량을 보고 배울 수 있는 리더와 동료가 있다면 큰 도움이 된다. 이런 리더와 동료가 주변에 있다면 운이 좋은 것이다. 하지만 운이 좋다고 실력이 늘지는 않는다. 주변에 배울 만한 사람이 있다고 해서 내 실력이 절로 늘지는 않는다는 얘기다. "자신의 교육은 스스로 책임진다" 결국 역량 향상은 스스로 책임져야 한다. 스스로 노력하지 않으면 역량은 늘지 않는다.

 

728x90
Comments