studybook
  • Introduction
  • 실무 하며 깨닫는 부분 정리
    • 옵션에 대해서
    • 코드 작성의 순서
    • 자바 프로그램에 문제가 생겼다면
    • 장애 대처법
  • Logstash, Beats 정리
  • Zookeeper 정리
  • Message Queue 정리
    • RabbitMQ 삽질
  • Java 관련 정리
    • Java Primitive Wrapper class
    • Java NIO
    • Java8 Double colon operator
    • Effective Java
      • 4장
      • 5장
      • 6장 - Enum, Annotation
      • 7장 - Method
      • 8장 - 프로그래밍 일반
      • 9장 - Exception
    • Java8 Lambda expression
    • JDBC
    • Linux에서 WatchService 이상동작
  • Spring 관련 정리
    • Spring Bean init, destroy 순서
    • Spring Async Controller
    • Spring Executable jar 웹 개발 및 배포
    • Spring Boot Font 배포 에러
    • Spring AOP
      • Spring AOP로 모든 Request 로그 남기기
    • Spring Cache
    • Spring Cloud
      • Consul로 spring 설정 관리하기
    • Spring Test
      • Spring Test DirtiesContext
      • Spring Test MockBean, SpyBean
      • Spring Test Dynamic @Scheduled
    • Spring JDBC
    • Spring Validation
    • Spring Transaction Management
      • Spring with JTA 삽질
    • Spring에서 효율적으로 Static resource 관리하기
    • Zuul을 사용해서 Spring Reverse proxy 만들기
    • Spring Security
    • 스프링 어노테이션이 안 먹힐 때 의심해볼만한 것
    • Spring Data
    • Spring Webflux
      • Tobi 강연
  • 코드 리팩토링
    • 한번에 하나씩
  • 지속적 통합 (CI)
    • Jenkins pipeline 삽질기
  • Log Aggregator 정리
    • Flume 테스트
    • Fluentd 테스트
  • Web Socket 정리
  • Akka
    • Actor 모델
    • Supervision
  • IE 8 대응 정리
  • 함수형 프로그래밍
    • 모나드
  • Netty
    • Netty 기본 예제
    • Netty 주요 특징
    • Netty 부트스트랩
    • Netty 채널 파이프라인, 코덱
    • Netty 이벤트 모델
    • Netty 바이트 버퍼
  • 스칼라 관련 정리
    • Maven으로 컴파일하기
    • Scala def 괄호 여부의 차이
    • 스칼라 function, method 차이점
    • ScalaTest와 Spring 연동하기
    • Programming in Scala
  • J2S 컨퍼런스
  • Android
    • 테스트
    • NDK
  • DDOS
  • HTTP
  • HttpClient
  • Container
    • Image 개요
    • cri-o
    • kata containers
    • Open Container Initiative Image
    • Buildkit
  • Github pages
  • Static Website
  • Webhook
  • Service Discovery Tools
    • Etcd
    • Eureka
    • Consul
      • ACL
    • 비교
  • React
    • JSX
    • React Element
    • Components, Props
    • State, Lifecycle
    • Handling Event
    • Flux
  • Vagrant
    • SSH 접속
  • Linux
    • Systemd
    • Alternatives
  • Messaging protocols
    • XMPP
    • AMQP
  • Windows
    • Windows10 내장 우분투에 ssh 클라이언트로 접속하기
    • Windows10 Hyper-V와 Virtual Box가 충돌을 일으켰을 때
    • Hyper-V 기반 docker에서 Shared Drives 설정 실패할 때
    • 윈도우 개발환경 설정
    • Docker desktop 없이 docker 환경 세팅하기
    • UWP 앱을 항상 관리자권한으로 실행하는 바로가기 만들기
  • Spring camp 2017
    • Project Reactive
    • 이벤트 소싱
    • CQRS
  • Spring webflux
  • 리액티브 프로그래밍
  • Linux Settings
    • 홈서버 백업 및 복구기
    • 홈서버 트러블슈팅
  • Kubernetes
    • k3s 설치 및 삽질
    • pod resources
    • Argo workflow
    • 트러블 슈팅
      • Kubernetes namespace의 phase가 Terminating에서 멈춰있을 때
    • 쿠버네티스 마스터
    • Knative
    • Knative Pipeline
    • Aggrerated API server
    • Accessing the API
      • Authenticating
  • Sonarqube
  • HTTP/2
  • Go
    • Go Module
    • Go dependency injection
    • Go Error handling
    • Go in Action
      • 3장 패키지
      • 4장 배열, 슬라이스, 맵
      • 5장 GO의 타입 시스템
      • 6장 동시성
      • 7장 동시성 패턴
      • 8장 표준 라이브러리
      • 9장 테스트와 벤치마킹
    • Go Channel 사용법
  • Cloud Native
Powered by GitBook
On this page
  • 작업은 작을 수 있다.
  • 객체에서 값 추출하기
  • 더 큰 예제
  1. 코드 리팩토링

한번에 하나씩

Previous코드 리팩토링Next지속적 통합 (CI)

Last updated 7 years ago

  • '한 번에 한 가지' 일만 하도록 논리적 영역 단위로 코드를 재조직하라

  1. 모든 작업을 나열해보아라

  2. 그 작업들을 서로 다른 함수로 쪼개어도 좋고, 최소한 한 함수 안에서라도 구분해서 놓아두라.

작업은 작을 수 있다.

// 투표는 '추천', '반대', '' 세 가지 경우의 수를 가진다.
// 이 함수는 기존의 투표를 수정하는데 사용한다.
var voteChanged = function(oldVote, newVote) {
  var score = getScore();

  if (newVote !== oldVote) {
    if (newVote === 'Up') {
      score += (oldVote === 'Down' ? 2 : 1);
    } else if (newVote === 'Down') {
      score -= (oldVote === 'Up' ? 2 : 1);
    } else if (newVote === '') {
      score += (oldVote === 'Up' ? 1 : -1);
    }
  }

  setScore(score);
}

작업 목록 1. old_vote와 new_vote가 각각 수치값으로 해석된다. 2. 수치값을 반영하여 총 점수가 변경된다.

// vote 값에서 수치값 획득
var getVoteValue = function(vote) {
  switch(vote) {
    case 'Up':
      return +1;
    case 'Down':
      return -1;
    default:
      return 0;
  }
}
// 수치값을 기준으로 수정
var voteChanged(oldVote, newVote) {
  var score = getScore();

  score -= getVoteValue(oldVote) // 이전 값 제거
  score += getVoteValue(newVote);

  setScore(score);
}

객체에서 값 추출하기

var location_info = {
  LocalityName: "",
  SubAdministrativeAreaName: "",
  AdministrativeAreaName: "",
  CountryName: ""
}
  • 목표 값은 LocalityName + ", " + CountryName

  • LocalityName이 없으면 SubAdministrativeAreaName, SubAdministrativeAreaName도 없으면 AdministrativeAreaName로 대체한다고 가정

var place = location_info["LocalityName"];
if (!place) {
  place = location_info["SubAdministrativeAreaName"];
}
if (!place) {
  place = location_info["AdministrativeAreaName"];
}
if (!place) {
  place = "Middle-of-Nowhere";
}
if (location_info["CountryName"]) {
  place += ", " + location_info["CountryName"];
} else {
  place += ", Planet Earth";
}

그런데 만약에 미국일 때만 CountryName 대신에 AdministrativeAreaName를 써야한다면?

작업 목록 1. location_info 객체에서 값을 읽어 저장하기. 2. '도시'의 값을 정하기 위해서 정해진 순서대로 탐색. 모두 비어있다면 '아무 곳도 아닌 곳'이라는 기본값을 입력 3. '나라' 값을 정함. 비어있다면 '지구'로 설정 4. place 변경

// 필요한 값들의 저장
var town = locationInfo.LocalityName;
var city = locationInfo.SubAdministrativeAreaName;
var state = locationInfo.AdministrativeAreaName;
var country = locationInfo.CountryName;

// '도시' 값 설정
// 기본값에서 시작하여 더 자세한 값으로 파고들어라
var firstHarf = "Middle-of-Nowhere";
if (state && country !== "USA") {
  firstHarf = state;
}
if (city) {
  firstHarf = city;
}
if (town) {
  firstHarf = town;
}

// '나라' 값 설정
var secondHarf = "Planet Earth";
if (country) {
  secondHarf = country;
}
if (state && country === "USA") {
  secondHarf = state;
}

return firstHarf + ", " + secondHarf;

더 큰 예제

  • 웹 크롤링 시스템에서 각 웹페이지를 호출해서 모두 받고 나면 호출되는 함수

    function updateCounts(httpDownload) {
    counts["ExitState"][httpDownload.exitState()] += 1;
    counts["HttpResponse"][httpDownload.httpResponse()] += 1;
    counts["Content-Type"][httpDownload.contentType()] += 1;
    }
  • 그러나 httpDownload가 위와 같은 메소드들을 제공하지 않음

  • 또한 값이 아예 존재하지 않을 수도 있음.

function updateCounts(httpDownload) {
  // 값이 있으면 exitState 값을 찾는다
  if ((httpDownload.hasEventLog() == false) || (httpDownload.hasExitState() == false)) {
    counts["ExitState"]["unknown"] += 1;
  } else {
    var stateStr = ExitStateTypeName(httpDownload.eventLog().exitState());
    counts["ExitState"][stateStr] += 1;
  }

  // 만약에 HTTP 헤더가 아예 없으면 unknown 추가
  if (httpDownload.hasHttpHeaders() == false) {
    counts["HttpResponse"]["unknown"] += 1;
    counts["Content-Type"]["unknown"] += 1;
    return;
  }

  // 값이 있으면 httpResponse를 기록
  var headers = httpDownload.httpHeaders();
  if (headers.hasResponseCode() == false) {
    counts["HttpResponse"]["unknown"] += 1;
  } else {
    var code = StringPrintf("%d", headers.responseCode());
    counts["HttpResponse"][code] += 1;
  }

  // 값이 있으면 Content-Type 값을 기록
  if (headers.hasContentType() == false) {
    counts["Content-Type"]["unknown"] += 1;
  } else {
    var contentType = ContentTypeMime(headers.contentType());
    counts["Content-Type"][contentType] += 1;
  }
}

작업 목록 1. 기본값 "unknown" 할당 2. httpDownload의 멤버 값 확인 3. 값을 읽어서 문자열로 변환 4. counts[] 갱신

function updateCounts(httpDownload) {
  // 1. 기본값 "unknown" 할당
  var exitState = "unknown",
      httpResponse = "unknown",
      contentType = "unknown";

  // 2 & 3. httpDownload에서 값을 확인하고 문자열로 변환
  if (httpDownload.hasEventLog() && httpDownload.eventLog().hasExitState()) {
    exitState = ExitStateTypeName(httpDownload.eventLog().exitState());
  }
  if (httpDownload.hasHttpHeaders() && httpDownload.httpHeaders().hasResponseCode()) {
    httpResponse = StringPrintf("%d", httpDownload.httpHeaders().responseCode());
  }
  if (httpDownload.hasHttpHeaders() && httpDownload.httpHeaders().hasContentType()) {
    contentType = ContentTypeMime(httpDownload.httpHeaders().contentType());
  }

  // 4. counts[] 갱신
  counts["ExitState"][exitState] += 1;
  counts["HttpResponse"][httpResponse] += 1;
  counts["Content-Type"][contentType] += 1;
}
  • 위의 개선된 코드에서 함수를 추출해내는 것도 좋은 방법.

function getExitState(httpDownload) {
  if (httpDownload.hasEventLog() && httpDownload.eventLog().hasExitState()) {
    return ExitStateTypeName(httpDownload.eventLog().exitState());
  } else {
    return "unknown";
  }
}