[기술세미나] 유지보수하기 좋은 코드를 구현하는 개발문화는 어떻게 만들 것인가?

hwwwa·2023년 4월 24일
0

✅ 일시 : 2023년 4월 24일(월) 14:00 - 16:00
✅ 연사 : 박재성 | 넥스트스텝 CEO

📌 목차

  1. 유지보수하기 좋은 코드를 구현하는 역량을 쌓기 위한 개인의 노력과 연습 방법
  2. 주니어(또는 중니어) 개발자가 자신이 쌓은 역량을 조직 내 동료들에게 전파하는 방법
  3. 리더(또는 시니어)가 조직 내 구성원들에게 긍정적인 영향을 미쳐 개발 문화를 만들어 가는 방법
    유지보수하기 좋은 코드를 구현하는 역량은 개발자와 회사의 중요한 관심사이다. 그럼에도 불구하고 이 역량과 문화를 만드는데 많은 어려움을 겪고 있다. 유지보수하기 좋은 코드를 구현하는 역량을 쌓고 좋은 개발 문화를 만들기 위한 방법을 개인, 주니어, 시니어(또는 리더) 관점에서 살펴본다.

🤔 변화는 어떻게 만들 것인가?

1. 개인 👤

의지력이 아닌 환경

  • 금세 고갈되어 사라질 의지력 대신 주변 상황(환경)의 조건을 살짝 바꿔 저절로 목표를 달성
    • <HABIT>
    • <최고의 변화는 어디서 시작되는가>
  • 주변 상황을 정리해 꾸준히 지속할 수 있는 환경을 구축
    • ex) 퇴근 후 스터디 카페로 퇴근, TV 치우기, 퇴근 후 스마트폰 끄기, 스마트폰에서 앱 삭제
  • 몰입할 수 있는 환경 만들기
    • 투자(교육, PT 등), 사회적 압력(공개적 약속), 나쁜 결과에 대한 상상, 책임감, 새로움
    • 책임감이 필요한 이유
      • 책임감이 있어야 우리 삶이 앞으로 나아가는 데 필요한 견인력을 얻을 수 있음

의식적인 연습

  • 유지보수하기 좋은 코드를 구현하는 좋은 습관 중의 하나는 테스트 주도 개발 (TDD)
    • 점진적 개발, 리팩토링 용이
  • TDD Circle of life
    • Test Fails
    • Test Passes
    • Refactor
  • TDD 실천이 어려움
    • 무의식적으로 프로덕션 코드를 먼저 작성하는 경우가 많음. Task 작성에 시간이 오래 걸림.
  • 무조건 연습을 많이 한다고 잘할 수 있을까?
    • 같은 것을 계속 반복한다고 해서 프로그래밍 역량이 높아지지 않음
  • TDD를 5, 6년 연습한 후에야 얻는 것
    • 테스트하기 쉬운 코드와 테스트하기 어려운 코드를 보는 눈
    • 테스트하기 어려운 코드를 테스트 하기 쉬운 코드로 설계하는 감
  • TDD를 효과적으로 연습하기 위해. 5-6년이 아닌 1-2년만에 역량을 높이기 위해
    • 목적의식 있는 연습
      • <1만 시간의 재발견>

의식적인 연습의 7가지 원칙

  • 효과적인 훈련 기법이 수립되어 있는 기술 연마
  • 개인의 컴포트 존에서 벗어난 지점에서 진행, 자신의 현재 능력을 살짝 넘어가는 작업을 지속적으로 시도
  • 명확하고 구체적인 목표를 가지고 진행
  • 신중하고 계획적. 개인이 온전히 집중하고 의식적으로 행동할 것을 요구
  • 피드백과 피드백에 따른 행동 변경
  • 효과적인 심적 표상을 만들어내는 한편으로 심적 표상에 의존
  • 기존에 습득한 기술의 특정 부분을 집중적으로 개선하여 발전시키고 수정하는 과정을 수반

메소드 분리 의식적 연습해보기

  • 문자열 덧셈 계산기 기존 코드
public class StringCalculator {
	public static int splitAndSum(String text) {
		int result = 0;
		if (text == null || text.isEmpty()) {
			result = 0;
		} else {
			String[] values = text.split(",|:");
			for (String value : values) {
				result += Integer.parseInt(value);
			}
		}
	}
}
  • indent 줄이기 👉 메서드 분리
    • indent가 1이 되도록 노력
public class StringCalculator {
	public static int splitAndSum(String text) {
		int result = 0;
		if (text == null || text.isEmpty()) {
			result = 0;
		} else {
			String[] values = text.split(",|:");
			result = sum(values);}
	}
	private static int sum(String[] values) {int result = 0;
		for (String value : values) {
			result += Integer.parseInt(value);
		}
		return result;
	}
}
  • else 없애기 👉 early return
public class StringCalculator {
	public static int splitAndSum(String text) {
		if (text == null || text.isEmpty()) {return 0;
		} 
		String[] values = text.split(",|:");
		return sum(values); 
	}
	private static int sum(String[] values) {
		int result = 0;
		for (String value : values) {
			result += Integer.parseInt(value);
		}
		return result;
	}
}
  • 메소드가 한 가지 일만 하도록 구현
    • 주로 코딩에서 리스트에는 200-300개만 있게 됨. 성능이 0.000n 밀리세컨드 떨어질 수는 있지만 최근에는 컴퓨팅 파워가 좋으므로 관계 없음
public class StringCalculator {
	public static int splitAndSum(String text) {
		if (text == null || text.isEmpty()) {
			return 0;
		} 
		String[] values = text.split(",|:");
		int[] numbers = toInts(values);return sum(numbers);
	}

	private static int[] toInts(String[] values) {int[] numbers = new Int[values.length];
		for (int i = 0; i < values.lengthl; i++) {
			numbers[i] = Integer.parseInt(values[i]);
		}
		return numbers;
	}

	private static int sum(int[] numbers) {int result = 0;
		for (int number : numbers) {
			result += number;
		}
		return result;
	}
}
  • 로컬 변수가 필요한지 판단하고 꼭 필요하지 않다면 제거
public class StringCalculator {
	public static int splitAndSum(String text) {
		if (text == null || text.isEmpty()) {
			return 0;
		} 
		return sum(toInts(text.split(",|:")));}

	private static int[] toInts(String[] values) {
		int[] numbers = new Int[values.length];
		for (int i = 0; i < values.lengthl; i++) {
			numbers[i] = Integer.parseInt(values[i]);
		}
		return numbers;
	}

	private static int sum(int[] numbers) {
		int result = 0;
		for (int number : numbers) {
			result += number;
		}
		return result;
	}
}
  • compose method 패턴 적용
    • 메소드(함수)의 의도가 잘 드러나도록 동등한 수준의 작업으로 쪼개기
public class StringCalculator {
	public static int splitAndSum(String text) {
		if (isBlank(text)) {return 0;
		} 
		return sum(toInts(split(text)));}

	private static boolean isBlank(String text) {return text == null || text.isEmpty();
	}

	private static String[] split(String text) {return text.split(",|:");
	}

	private static int[] toInts(String[] values) {
		int[] numbers = new Int[values.length];
		for (int i = 0; i < values.lengthl; i++) {
			numbers[i] = Integer.parseInt(values[i]);
		}
		return numbers;
	}

	private static int sum(int[] numbers) {
		int result = 0;
		for (int number : numbers) {
			result += number;
		}
		return result;
	}
}

클래스 분리 연습을 위해 활용할 수 있는 원칙

  • 모든 원시값과 문자열을 포장
  • 일급 콜렉션 쓰기
  • 3개 이상의 인스턴스 변수를 가진 클래스를 쓰지 않는다
  • 함수의 인자 수를 줄인다. 3개 이상은 가급적 피하기

<엘레강트 오브젝트>

  • 생성자 하나를 주 생성자로 만들기
  • 퍼블릭 상수 사용하지 않기
  • 불변 객체로 만들기
  • 모의 객체 대신 페이크 객체 사용
  • 5개 이하의 퍼블릭 메서드만 노출
  • 정적 메소드 사용 X
  • 절대 setter와 getter 하용하지 않기
  • 부 생성자 밖에서는 new 사용하지 않기

2. 개인(팀원 또는 중니어) ➡️ 팀 👥

  • 개인의 역량을 쌓은 후 팀과 동료에게 영향력을 전파
  • 토이 프로젝트로 TDD, 클린코드, OOP 연습을 하다 회사 코드로 리팩토링
  • 변화를 만들려면 리더쉽 역량을 발휘하고 감정 노동이 필요
  • 리더쉽 역량과 감정 노동은 인공지능 시대에 가장 필요한 역량

변화를 시도할 때 생각해 볼 점

  • 사람은 기본적으로 변화를 거부하는 성향을 가짐
  • 팀은 변화를 거부하는 성향이 더 강함
  • 대부분 사람들은 변화에 실패한 경험을 가지고 있음

시작하기

  • 내가 맡은 기능 구현/개선할 부분에만 RDD 또는 리팩토링 적용
  • 묵묵히 혼자 진행
  • 꼭 남을 설득하려하지 않기
  • 관심을 가지는 사람이 생기면 전파
  • 내가 구현한 코드 또는 동료의 관심에서 작은 성공을 맛본다
    • 처음부터 큰 성공을 이루려하지 않기

레거시 코드 리팩토링 사례

  • Layered Architecture
    • 핵심 비즈니스 로직은 어디에 구현하는 것이 나을까?
    • 레이어를 정확하게 나누어야 TDD 가능
  • 서비스 레이어에 로직을 구현하는 경우의 문제점
    • database 로직과 서비스 로직이 혼합되어 있으면 TDD 어려움
    • mocking하여 TDD하는 것은 좋지 않음. 테스트 코드 읽기 어려우며 구현도 어려움
    • OOP 코드를 구현하면 TDD하기 쉽고 유지보수 하기 쉬움
  • 서비스 레이어에 구현한 로직을 도메인 객체로 이동하려면?
    • 보호 후 수정하기!
    • mock framework로 단위 테스트를 만든 후 로직을 분리하고 no mock 단위 테스트코드 작성
    • 서비스 레이어의 코드는 매우 적어야 함. thin layer
      • 전달하는 역할만. 서비스 레이어에 비즈니스 로직을 구현하는 것은 좋지 않다.

3. 개인(리더 또는 시니어) ➡️ 팀 🗣👥

리더 또는 시니어로서 만들고 싶은 개발 문화

  • 지식을 공유하고 같이 성장하며 유지보수하기 좋은 코드를 구현하는 팀을 만들고 싶다
  • 팀의 변화를 만들려면 어디서부터 어떻게 시작하면 좋을까?
  • 아웃사이드 인 접근 방식
    • 개발자의 역량을 키우고, 소스 코드 품질 유지를 위해 온라인 코드 리뷰와 짝 프로그래밍을 팀의 문화로 정착시키고 싶다
    • ➡️ 실패의 지름길!!
    • 팀원들이 하는 척을 할 수는 있지만, 진정한 변화는 어려움

문화(변화)를 만들기 전에 생각해 볼 점

  • 사람은 기본적으로 변화를 거부하는 성향

  • 팀은 변화를 거부하는 성향이 강함

  • 대부분의 사람들은 변화에 실패한 경험을 가지고 있음

  • 오히려 팀장의 위치에서 변화를 만드는 것이 더 어려움

  • 팀원들은 침묵을 선택할 수 밖에 없음

    • 문제 제기 시 혜택을 얻기 어렵고 상당 시간이 지나야 혜택을 받을 수 있음

문화(변화)를 만드는 시작점

  • 팀의 심리적 안정감을 만들기
  • 심리적 안정감
    • 구성원이 업무와 관련해 그 어떤 의견을 제기해도 벌을 받거나 보복당하지 않을 거라고 믿는 조직 환경
    • 인사이드 아웃 접근 방식
    • 심리적 안정감을 만들며 문화 만들기 시작
    • 팀원들과의 신뢰 형성이 우선
    • 1:1 면담을 통해 개선할 부분 찾기. 1:1 면담은 중요함
  • 1:1 면담을 두려워하지 말기
    • 문제를 해결해주어야 할 것 같은 두려움 갖지 말기. 해답을 제시하려하지 말기
    • 어떻게 하면 될까? 너라면 어떻게 할 것 같아? 라는 반문을 통해 팀원들이 해답을 제시하도록 하기
    • 면담, 팀회고를 통해 우선순위가 높은 것을 선정하여 Practice
    • 선택한 Practice가 개인, 팀 모두 익숙해 질 때까지 한 가지에 집중
    • 선택한 Pracitce로 변화를 완료함으로써 작은 성공을 맛본다. 팀 전체가 함께 성공을 맛보는 것이 중요
    • 믿음은 서서히 생기는 것

  • 한 번에 한 가지에 집중하는 것이 문화(변화)를 만드는 가장 빠른 길이다
  • 새로운 문화를 정착하기 위해 가장 중요한 것은 리더의 인내심과 용기
    • 새로운 문화를 만들면서 초기 학습 비용 등으로 인해 생산성 저하
    • 안정화를 위해 최소 1년 이상의 시간을 투자해야 한다는 마음으로 믿고 기다리기
  • 중요한 것은 어떤 Practice를 적용하느냐가 아닌, 현재보다 조금씩 나아지고 있다는 방향성이 중요
  • 문화를 만들고, 변화를 만드는 일은 리더만의 책임이 아님

말만 잘듣고 시키는 것만 잘하는 인재가 아닌, 주도적인 인재가 지금의 인재상
실패해도 괜찮다. 실패하기 전보다는 나는 한 단계 성장한다. 좋은 회사는 실패해도 같이 도전하는 사람을 원한다.

가장 필요한 것은 가보지 않은 길에 꾸준히 도전할 수 있는 용기


💭 Q&A

  • 💭 만약 리더가 변하려고 하지 않는다면 조직원은 어떻게 해야하나요?

    • 문제를 정면 돌파하며 해결책을 만들고 도전해보고 시도 해보다 그만둬야한다. 그래야 이력서에 할 애기가 있음
      시도해보지 않고 이직한다면 그 회사에서도 이 사람은 이직 할 사람으로 보인다
      꼭 논리적으로 설득하려고 하지 않는다. 논리적으로 이기는 것이 꼭 좋은 것인가? 논리적으로 진 사람의 기분은?
      사람에 대한 존중, 신뢰가 이루어진 상태에서 변화를 만들려고 해야하지 않는가. 심리적 안정감이 중요
      이직은 정말 마지막 단계
  • 💭 업무와 리팩토링 중 어떤 것이 더 중요한가요?

    • 당연히 업무가 더 중요하다. 개인 연습을 통해 빠르게 리팩토링 할 역량을 만든다.
      역량을 쌓은 후 목소리를 내어 변화를 만드려고 노력한다. 설득할 필요가 있음. 대화를 해나가야 함
  • 💭 코드 작성 시 유지보수를 위해 가장 고려하는 부분은?

    • 읽기 좋은 코드 만들기.
  • 💭 어드민 위주의 개발만 하다보니 트래픽 많은 서비스의 경험이 없습니다. 커리어가 걱정되는데 어떻게 해야 할까요?

    • 이런 고민은 리더에게 상담해야 함. 다른 팀에 전배 할 기회 찾기. 여러 방법을 시도해보다 안된다면 마지막에 이직.
      관점을 바꿔 꼭 대용량 트래픽 경험이 1순위가 아닐 수 있음. 더 중요한 역량을 찾기.
      단순 반복적인 작업을 내 주도 하에 새로운 툴을 도입하여 개편해보고 ...
  • 💭 자주 사용되고 반복되는 코드는 분리하는 게 맞지만 그렇지 않다면 유지보수를 위해 어느 레벨까지 분리하는 것이 좋은가요?

    • 이러한 기준을 찾는 것이 전문가로 가는 여정. 정해진 답이 없음.

0개의 댓글