본문 바로가기
정보처리기사/필기

[정보처리기사] Part02-04-03. 애플리케이션 성능 개선 (2)

by 채연2 2023. 1. 16.

애플리케이션 성능 개선

  코드 최적화

 

 

애플리케이션 성능 개선

코드 최적화

소스코드 최적화 개요

소스코드 최적화 필요성
  • 읽기 쉽고 변경 및 추가가 쉬운 클린 코드 (Clean Code) 작성하는 것
  • 소스 코드 품질 위해 기본적으로 지킬 원칙과 기준 정의
  • 소스 코드 지속적으로 변경되므로 소프트웨어 구조와 실행 시간 및 자원 사용량 측면에서 비효율적인 형태로 변경 가능성 있음

 

나쁜 코드 (Bad Code)
  • 다른 개발자가 로직(Logic) 이해하기 어렵게 작성된 코드
    • 처리 로직의 제어가 정제되지 않고 서로 얽혀 있는 스파게티 코드
    • 변수나 메소드에 대한 이름 정의를 알 수 없는 코드
    • 동일 처리 로직이 중복되게 작성된 코드 등

 

외계인 코드 (Alien Code)
  • 아주 오래되거나 참고 문서 또는 개발자가 없어 유지 보수 작업이 어려운 코드

 

클린 코드 (Clean Code)

  • 잘 작성되어 가독성 높고, 단순하며, 의존성 줄이고, 중복 최소화하여 깔끔하게 잘 정리된 코드
  • 클린 코드가 많을 수록 애플리케이션 설계 개선됨
  • 가독성 높으므로 유지보수비 낮아지고 변경 속도 빠름
원칙 설명
가독성 이해하기 쉬운 용어 사용
코드 작성 시 들여쓰기 기능 사용
단순성 한 번에 한 가지 처리만 수행
클래스 / 메소드 / 함수 최소 단위로 분리
의존성 영향도 최소화
코드 변경이 다른 부분에 영향 없게 작성
중복성 중복 코드 제거
공통 코드 사용
추상화 클래스 / 메소드 / 함수에 대해 동일 수준의 추상화
상세 내용은 하위 클래스 / 메소드 / 함수에서 구현

 

 

소스코드 최적화 기법

01 클래스 분할 배치 기법

클래스는 하나의 역할, 책임만 수행 가능토록 응집도 높이고 크기 작게 작성

 

02 클래스 간 느슨한 결합 (Loosely Coupled) 기법

클래스 자료 구조, 메소드를 추상화할 수 있는 인터페이스 클래스 이용하여 클래스 간 의존성 최소화

public class Point {
   public double x;
   public double y;
}
  추상화된 자료구조로 의존성 최소화 구현 (Loosely coupled)
- x, y 값이 public 이므로 사용자가 x, y 값 입력 후 해당 클래스 세부 구현 조작 가능

☞ Point 설정 시 항상 x, y 두 값을 한 번에 설정 가능토록 해야 함
 
public interface Point { 
   public double getX(); 
   public double getY(); 
   
   void setRectangular(double x, double y);
}

 

03 코딩 형식 기법

  • 코딩 시 줄바꿈 (Indentation)을 확실히 지켜야 함
  • 호출하는 함수 먼저 배치하고, 호출되는 함수 나중에 배치
  • 지역 변수 선언 위치는 각 함수 맨 처음에 선언
  • 함수 간 주고 받는 파라미터는 이름만 보고도 어떠한 파라미터인지 파악 가능토록 이름 부여
  • 변수명은 기억하기 좋은 이름, 발음이 쉬운 용어, 접두어 사용 등 기본적인 명명 규칙 (Naming Rule) 정의

 

04 주석문 사용 활성화

코딩 시 항상 주석문 사용하여 다른 개발자가 이해하기 쉽도록 설명

 

05 프로그램 호출 순서 조정을 통한 성능 개선

  • 호출하는 함수 먼저 코딩하고, 호출되는 함수 나중에 배치
public class SeqTest {
   public static void main(String[] args) {
      Seq seq = new Seq();
      
      System.out.println(seq.cWithDrop(100));
      System.out.println(seq.cWithDrop(100));
      
      int amount = 100; 
      if (seq.cWithDrop(amount) == -1) { 
         OverDrop();
      } else { 
         UsualThing();
      }
   }
}
 
if (seq.cWithDrop(amount) == -1) {
     OverDrop();
} else {
     UsualThing();
}

// caller 1 : OverDrop()
// caller 2 : UsualThing()
 
// caller 1
private static void OverDrop() {
   System.out.println("Overdrop()");
}

// caller 2
private static void UsualThing() { 
   System.out.println("UsualThing()");
}

 

  • 루프 내 메소드 호출이 반복되는 코드를 루프 진입 전 호출하도록 소스 코드 수정
public void loopSample() {
   TrSet trSetSample = null; 
   trSetSample = trSetChogi; 
   CompareTimer timer = new CompareTimer("loopSample"); 
   
   if (trSetSample != null) { 
      int[] treeNum = new int[trSetSample.size()];
      
      /**********************************************/ 
      for (int i=0; i<trSetSample.size(); i++) { 
         treeNum[i] = (int) trSetSample.toArray()[i]; 
      }
      /***********************************************/
   } 
   
   timer.checkCurrentTimeMillis(); 
}
  ▲ 루프 내에서 함수를 계속 호출하는 코드  
▼ 루프 내에서 함수를 호출하지 않는 코드로 성능 개선 가능
public void loopSample() {
   TrSet trSetSample = null; 
   trSetSample = trSetChogi; 
   CompareTimer timer = new CompareTimer("loopSample"); 
   
   if (trSetSample != null) { 
      int[] treeNum = new int[trSetSample.size()]; 
      int trSetSampleSize = trSetSample.size(); 
      Iterator<Integer> iterator = trSetSample.iterator(); 
      
      /**********************************************/ 
      for (int i=0; i<trSetSampleSize; i++) { 
         treeNum[i] = (int) iterator.next(); 
      }
      /***********************************************/
   }
   
   timer.checkCurrentTimeMillis();
}

 

 

리팩토링 (Refactoring) 기법

개념

유지보수 생산성 향상을 목적으로 기능 변경하지 않고 복잡한 소스 코드를 수정 및 보완하여 가용성 및 가독성을 높이는 기법. 소프트웨어 모듈 외부적 기능은 수정하지 않고 내부적으로 구조, 관계 등을 단순화하여 소프트웨어 유지 보수성을 향상시키는 기법

 

리팩토링 수행 대상

나쁜 코드, 외계인 코드를 포함한 정리가 필요한 코드들에 대해 소스 리팩토링 수행 가능

코드 증상 설명 리팩토링
중복된 코드 한 곳에서 중복된 코드 발견된 증상으로 기능이나 데이터 코드 중복 중복 제거
긴 메소드 메소드 너무 길어 이해 어렵고 가독성 떨어지는 증상 메소드 적정 수준 크기로 줄임
큰 클래스 한 클래스에 너무 많은 속성과 메소드 존재 클래스 몸집 줄임
긴 파라미터 리스트 이해하기 어렵고 일관성 없거나 사용하기 어려움 파라미터 개수 줄임
두 가지 이상 이유로
수정되는 클래스
(Divergent Class)
한 클래스 메소드가 2가지 이상 이유로 수정이 되면, 그 클래스는 한 가지 종류 책임만을 수행하는 것이 아님 한 가지 이유만으로 수정되도록 변경
여러 클래스 동시 수정
(Shotgun Surgery)
특정 클래스 수정하면 그때마다 관련된 여러 클래스들 내에서 자잘한 변경 수행 여러 클래스에 흩어진 유사 기능을 한 곳에 집결
다른 클래스 지나치게 애용
(Feature Envy)
빈번히 다른 클래스로부터 데이터 얻어와 기능 수행 메소드를 그들이 애용하는 데이터가 있는 클래스로 이동
유사 데이터들 그룹 중복
(Data Clumps)
3개 이상 데이터 항목이 여러 곳에 중복되어 나타남 해당 데이터들을 독립된 클래스로 정의
기본 데이터 타입 선호
(Primitive Obsession)
객체 형태 그룹 만들지 않고 기본 데이터 타입만 사용 같은 작업 수행하는 기본 데이터 그룹을 별도 클래스로 생성

 

리팩토링 주요 기법
기법 대상 설명
Move Method 클래스를 나누어 의존성 줄임
Field 빈도 높은 클래스 멤버 변수로 필드 이동
Extract Class 데이터 및 기능 분리하여 별도 클래스 생성
Method 중복된 코드 추출하여 메소드로 생성
Rename Method 목적과 이름이 다른 경우 이름 변경
Field 변수 용도와 이름 상이한 경우 변경

 

리팩토링 사례
  • 적용 전 소스 코드는 1 ~ 10까지의 더한 결과 값과 파라미터로 전달 받은 데이터 계산을 하는 로직이 메소드들마다 존재
  • 리팩토링 Extract Method 기법 활용하여 1 ~ 10까지 계산하는 로직은 공통 메소드로 추출하여 소스 유지 보수성 높임
리팩토링 전 소스 리팩토링 후 소스
public int addData(int param) {
   int result = 0; 
   
   for(int i=1; i<=10; i++) { 
      result = result + i;
   }
   
   return result + param;
}

public int minusData(int param) { 
   int result = 0; 
   
   for(int i=1; i<=10; i++) { 
      result = result + i;
   } 
   
   return result - param; 
}
public int addData(int param) { 
   return cal() + param;
}

public int minusData(int param) { 
   return cal() - param;
} 

private int cal() { 
   int result = 0;
   
   for(int i=1; i<=10; i++) {
      result = result + i;
   }
   
   return result;
}

 

리팩토링 수행 시 주의 사항
  • 변경된 소스에 대한 형상 관리 및 필요 시 원상 복구 처리
  • 리팩토링 수행 후 회귀 테스트 (regression testing 실시)
  • 과도한 리팩토링으로 서비스 장애 우려 가능
  • Legacy System의 Tailoring, 도구를 이용한 리팩토링 시 효율 극대화

 

 

 

 

320x100

댓글