나는 잘하는 개발자인가?

이선주·2024년 12월 22일
6

회고

목록 보기
3/5
post-thumbnail

'개발을 잘한다'는 무슨 뜻일까요? 일차원적으로 생각했을 때 코드 작성, 문서화, devOps 등이 있을 수 있겠지만, "시니어 개발자 노하우 - 개발 잘하는 방법" 세션에서는 '문제 정의와 전략'을 말씀해주셨습니다. 즉, 개발을 잘하려면 문제를 잘 다루어야 하는 것이지요.

아래는 이번 회고의 간단한 요약입니다.

문제 정의란 무엇인가?
 - 문제를 해결하기 위해 올바르게 이해하는 과정, '잘못된 문제 정의는 실패로 가는 지름길'


문제 정의와 접근

- 문제 정의는 프로젝트 성공의 핵심 출발점으로, 문제를 올바르게 이해하고 해결하는 과정이 필수적
- 5 Why's 분석과 문제 정의 Canvas를 활용하여 최소 정의(MVP)를 도출
- 계획은 구체적이고, 예측 가능하며, 달성 가능하고, 현실적이며, 명확한 기한이 있어야 함

문제 해결 전략

- 복잡한 문제는 작은 단위로 분해하여 관리 가능한 크기로 접근
- 추상화 레벨 조절을 통해 큰 그림과 세부 사항의 균형을 맞추며, 이해관계자와 협력 필요

코드 품질과 리뷰

- 디자인 패턴, 아키텍처는 결국 코드 품질을 위한 도구일 뿐, 도구가 우선순위가 되어서는 안됨
- 좋은 코드는 간결하고, 가독성이 좋으며, 확장이 용이해야 함
- 코드 리뷰를 통해 품질 향상, 지식 공유, 협업 강화 및 유지보수성 개선
- 리뷰는 버그 감소와 팀워크 향상에 기여하지만, 리뷰 속도를 무작정 높이는 것은 불가능

이후부터는 평어체로 작성하겠습니다.


1. 정의와 접근

문제를 제대로 정의하고 접근하려면 무엇이 문제인지, 왜 문제가 발생했는지, 그리고 문제를 해결했을 때 어떤 결과를 기대하는지를 명확히 하는 것이 중요하다.

✅ 문제 정의의 핵심요소

  • 무엇이 문제인가?
  • 왜 문제가 발생하였는가?
  • 문제를 해결했을 때 어떤 결과를 기대하는가?

✅ 문제 정의의 나쁜 예를 하나 들어보자.

“사용자가 카드 결제를 할 수 있었으면 좋겠어요”라는 요구사항이 들어왔을 때, 단순히 카드 결제 기능만 구현하면 추가적인 리소스가 발생할 가능성이 높다. 예를 들어 카드 결제 기능을 구현한 뒤에 “간편 결제는 왜 안 되나요?”라는 피드백이 들어올 수 있다. 이는 사용자 입장에서 카드 결제, 간편 결제, 계좌이체가 모두 같은 결제 수단으로 인식되기 때문이다. 이러한 상황은 결국 추가 리소스와 비용을 발생시키고, 사용자 신뢰도에도 부정적인 영향을 줄 수 있다.

이 문제가 발생한 이유는 무엇일까? 바로 “카드 결제”에만 초점을 맞췄기 때문이다. 요구사항 이해와 커뮤니케이션 부족으로 인해 문제를 단순히 표면적으로만 인식한 결과다.

✅ 내가 생각한 것이 정답이 아닐 수 있다.

내가 생각한 것만이 정답이 아니다. 문제를 여러 측면에서 탐구하고 정리하며, 팀원 및 이해관계자와 적극적으로 소통해야 한다. 이를 통해 요구사항을 명확히 이해할 수 있게 된다. 이를 위해서는 초기 단계에서 문제 정의에 충분한 시간과 노력을 투자하는 것이 필수적이다.

1.1. 계획 수립

계획은 구체적이고, 예측 가능하며, 달성 가능하고, 현실적이며, 명확한 기한이 있어야 한다.

📌 현실적인 계획
말 그대로 계획은 어떠한 공상도 없이 현실적이어야 한다.
예를 들어, 계획을 수립할 때 애매하거나 내가 모르는 내용이 있을 수 있다. 이 미지의 영역을 “어떻게든 되겠지” 또는 “그때가서 보자”는 식으로 넘겨 짚어서는 안된다.

✅ 문제 정의를 도와주는 툴

일을 효율적으로 하는 것은 시스템을 만드는 것이 중요하다고 생각한다. 아래 도구들은 사이드 프로젝트에 적용해보면서 실무에 차츰 적립해나갈 생각이다.

  • 5 Why's 분석
  • 문제 정의 Canvas를 활용하여 최소 정의(MVP)를 도출

2. 문제 해결 전략

바라보는 관점에 따라 문제가 쉽거나 어려워질 수 있다고 한다.

문제를 그대로 본다면 단면만을 볼 것이다. 하지만, 여러가지 측면 문제를 바라본다면? 숨겨진 요구사항 등을 알 수 있을 것이다.

또는 문제를 작은 단위로 쪼갤 수 있을 것이다.

✅ 문제 해결 전략

  1. 복잡한 문제는 작은 단위로 분해하여 관리 가능한 크기로 접근
  2. 추상화 레벨 조절을 통해 큰 그림과 세부 사항의 균형을 맞춤
  3. 이해관계자와 협력하여 적절한 추상화 레벨 조정
  4. 가설로 접근하여 문제를 명확하게 설정
  5. 디버깅과 회고 작성

📌 실패를 숨기지 말자
버그가 발생하지 않는 소프트웨어는 없다.
실패를 통해 배우고 회고하여 공유하자.

3. 최적의 코드 작성

최적의 코드는 간결하고, 가독성이 좋으며, 확장성이 뛰어난 코드를 말한다.

이런 코드를 작성하는 건 단순해 보이지만 실제로는 상당히 어렵다고 느낄 때가 많다.

최근 코드를 작성하면서, 나도 모르게 구조적인 고정 관념에 사로잡혀 있었던 적이 있다. 예를 들어, 간단한 과제나 예제 코드에서도 클린 아키텍처를 지나치게 따르려 하거나, 요구사항이 단순함에도 불구하고 유즈케이스를 작성하려고 했던 것 같다. 이렇게 뭔가 있어 보이는 코드를 작성하려다 보니 불필요하게 복잡해지는 경우가 있었다.

그렇다면, 간결하면서도 가독성이 좋고 확장성까지 뛰어난 코드란 어떤 코드일까?

✅ DRY (Don’t Repeat Yourself):
중복된 코드를 피하고, 동일한 로직은 한 곳에서 관리하도록 작성하라

// BEFORE: 중복된 코드
function calculateCircleArea(radius: number) {
  return Math.PI * radius * radius;
}

function calculateCircleCircumference(radius: number) {
  return 2 * Math.PI * radius;
}

// AFTER: DRY 원칙 적용하여 응집도를 높힌다.
class CircleUtils {
  static area = (radius: number) => Math.PI * radius ** 2;
  static circumference = (radius: number) => 2 * Math.PI * radius;
};

console.log(circleUtils.area(5));
console.log(circleUtils.circumference(5));

✅ KISS (Keep It Simple, Stupid):
코드를 간견하고 직관적으로 작성하라
복잡한 코드는 이해하기 어렵고, 유지보수나 디버깅을 어렵게한다.

// BEFORE: 복잡하고 난해한 코드
function getUserMessage(status: string) {
  if (status === 'active') {
    return 'Welcome back!';
  } else if (status === 'inactive') {
    return 'Please activate your account.';
  } else if (status === 'banned') {
    return 'Your account is banned.';
  }
  return 'Unknown status.';
}

// AFTER: KISS 원칙 적용
const messages = {
  active: 'Welcome back!',
  inactive: 'Please activate your account.',
  banned: 'Your account is banned.',
};

function getUserMessage(status: string) {
  return messages[status] || 'Unknown status.';
}

// 사용 예
console.log(getUserMessage('active'));
console.log(getUserMessage('unknown'));

✅ 리펙토링:
테스트 코드를 통해 검증하고, 테스트 후 진행 및 검토하라

// BEFORE: 중복되고 복잡하고 난해한 코드
class OrderCalculator {
  calculate(items: Item[], coupon: Coupon) {
    let total = items.reduce<number>((sum, item) => sum + item.amount, 0);
    
    // 쿠폰 할인 적용
    // IF: 지금은 현금 할인이지만, 비율 할인을 적용하려 한다면?
    if (order.coupon) {
      total -= order.coupon.discount;
    }
    
    return total;
  }
}

// AFTER: DRY & KISS 원칙 적용
class Order {
  private total: number;
  
  get total() {
    return this.total;
  }

  constructor(
    private readonly items: Item[],
    private readonly coupon: Coupon,
  ) {
    const total = items.reduce<number>((sum, item) => sum + item.amount, 0);
    const discountTotal = coupon.apply(total);

    this.total = discountTotal;
  }
}

// 다형성을 이용하여 DRY 및 KISS 원칙을 적용
interface Coupon {
  apply(price: number): number;
}

// 현금 할인 쿠폰
class AmountDiscountCoupon implements Coupon {
  constructor(
    private readonly discountAmount: number
  ) {}

  apply(price: number) {
    return price - this.discountAmount;
  }
}

// 비율 할인 쿠폰
class RateDiscountCoupon implements Coupon {
  constructor(
    private readonly discountRate: number
  ) {}
  
  apply(price: number) {
    return price - price * discountRate;
  }
}

4. 코드 리뷰

코드 리뷰를 통해 얻을 수 있는 장점은 아래와 같다.

  • 코드 품질 향상
  • 학습 및 지식 공유
  • 팀 협업 및 소통 향상
  • 유지보수 용이
  • 개발 속도 향상

나는 사내에 코드 리뷰 문화를 적립시키려고 노력중이다. 덕분에 실제로 코드 품질이 개선되고, 팀 내 지식 공유가 활성화되며, 협업과 소통이 더욱 원활해졌다.

그렇다면, 유지보수성과 개발 속도는 어떻게 향상될까? 코드 리뷰를 통해 초기에 버그를 발견하고 해결할 수 있다면 유지보수가 훨씬 수월해진다. 유지보수성이 높아지면 자연스럽게 개발 속도 또한 개선된다.

이처럼 코드 리뷰는 거의 모든 면에서 긍정적인 효과를 준다. 문제는 팀 내 코드 리뷰 문화가 제대로 자리 잡지 않은 경우다. 팀원 모두가 코드 리뷰의 중요성을 이해하고 동참해야만 이러한 장점을 온전히 누릴 수 있다.

또한, 코드 리뷰는 훈련이 필요하다. 처음부터 완벽한 리뷰를 기대하기는 어렵다. 도입 초기에는 리뷰에 시간이 오래 걸릴 수 있지만, 이는 단점이라기보다 장기적인 투자로 봐야 한다. 리뷰에 시간을 투자하면 유지보수성과 개발 속도가 개선될 뿐 아니라, 특정 에이스에 의존하지 않는, 서로를 대체할 수 있는 건강한 조직을 만들 수 있다.

결론적으로, 코드 리뷰의 장점을 극대화하려면 팀원 모두가 이 문화를 이해하고, 올바른 리뷰를 할 수 있도록 훈련이 필요하다.

마지막으로, 협업의 중요성도 빼놓을 수 없다. 혼자서 무언가를 끝까지 해내는 것은 쉽지 않다. 어려움에 부딪힐 때 옆에 있는 동료들이 밀어주고 끌어줄 때 비로소 목표를 달성할 수 있다고 믿는다.

profile
백엔드 개발자의 기초 다지기

0개의 댓글