코딩오류, 코드스멜, 리팩토링

개념
코딩 오류 정의 코딩은 모듈에 대한 원시코드 작성하고 오류를 검출하는 단계로 코딩오류는 설계 명세상의 누락이나 프로그래밍 기법상의 결함 지칭 코드 스멜 정의 Kent Beck에 의해 처음 제시되어 프로그램 가독성이 나쁘고 중복된 로직을 포함하는 등 코드품질을 저하시키는 요인들을 총칭(ex-길다, 많다, 겹쳐있다, 이름이 어색하다, 객체지향적이지 않다 등 리팩토링 정의 코딩 오류 가능성을 최소화하기 위해 프로그램 내에서 이해하기 어렵고 수정하기 힘들며, 확장하지 어려운 코드(=코드 스멜)를 원래 기능을 그대로 두면서 내부구조를 개선하는 활동

1. 안전한 소프트웨어 개발 위한 코딩의 개요

  가. 코딩 오류 정의

코딩은 모듈에 대한 원시코드 작성하고 오류를 검출하는 단계로 코딩오류는 설계 명세상의 누락이나 프로그래밍 기법상의 결함 지칭

  나. 코드 스멜 정의

Kent Beck에 의해 처음 제시되어 프로그램 가독성이 나쁘고 중복된 로직을 포함하는 등 코드품질을 저하시키는 요인들을 총칭(ex-길다, 많다, 겹쳐있다, 이름이 어색하다, 객체지향적이지 않다 등

  다. 리팩토링 정의

코딩 오류 가능성을 최소화하기 위해 프로그램 내에서 이해하기 어렵고 수정하기 힘들며, 확장하지 어려운 코드(=코드 스멜)를 원래 기능을 그대로 두면서 내부구조를 개선하는 활동

 

2. 코딩오류, 코드스멜, 리팩토링 관계도 및 종류

  가. 코딩오류, 코드스멜, 리팩토링 관계도

  나. 코딩 오류의 종류

구분

내용

메모리 누수

- 메모리가 Free되지 않고 프로그램에 계속 할당되는 문제

char *   foo(int s) {

    char  *output;

    if ( s > 0 )

          output = (char *) malloc (size);

    if ( s == 1 )

         return  NULL;   /* if  s == 1 then memory leaked  */

    return(output);

}

중복된 Free

선언

- 이미 Free로 소멸된 자원을 또다시 Free로 선언하는 경우

main() {

    char  *str;

    str = ( char * ) malloc (10);

    if ( global == 0 )

        free(str);

    free(str);  /*  str is already freed

}

NULL의 사용

- NULL을 포인트하고 있는 곳의 콘텐츠를 접근하려 하면 오류

- Alias를 사용하는 경우 유의

char  *ch = NULL;

if ( x > 0 ) {

    ch = 'c';

}

printf("\%c", *ch);  //  ch may be NULL

*ch = malloc(size);

ch = 'c';  // ch will be NULL if malloc returns NULL

별칭의 남용

- 서로 다른 주소값을 예상하고 사용한 두 변수가 Alias로 선언된 경우

예) strcat(src, destn)

         src와 destn이 alias로 선언된 경우

배열 인덱스

오류

- 인덱스 한도를 벗어난 배열

dataArray[80];

for ( i=0; i <= 80; i++ )

        dataArray[i] = 0;

수식예외 오류

- 0으로 나누는 오류, 변동 소수점 예외 오류

하나 차이에

의한 오류

예)  < N                     <=  오류

   if( number = SSN ) {      == 의 오류

사용자 정의

자료형 오류

typedef  enum {A, B, C, D} grade;

void foo(grade x) {

    int  l, m;

    l = GLOBAL_ARRAY[x - 1];   // Underflow possible 

    m = GLOBAL_ARRAY[x + 1];  // Overflow possible

}

스트링 처리

오류

- strcpy, sprintf 등 많은 스트링 처리 함수

- 매개 변수가 NULL이거나 스트링이 ‘\0’이 아니거나

버퍼 오류

- 버퍼 오류 시 리턴 주소가 반환되는 것을 이용하여 해킹에 이용될 수 있음

void mygets(char *str) {

    int ch;

    while ( ch = getchar() != '\n' && ch != '\0' )

        *(str++) = ch;

    *str = '\0';

}

main () {

    char  s2[4];

    mygets( s2 );

}

동기화 오류

- 다수의 스레드가 있는 병렬 프로그램에서의 오류

데드락

- 다수의 스레드가 서로 자원을 점유하고 릴리스 하지 않음

레이스 컨디션

- 두 개의 스레드가 같은 자원을 접근하려 하여 수행 결과가 스레드들의 실행 순서에 따라 다르게 되는 경우

모순 있는

동기화

- 공유하는 변수를 접근 할 때 로킹과 언로킹을 번갈아 할 때 일어나는 오류

 

  다. 코드 스멜의 종류

코드 스멜

설명

리팩토링

중복된 코드

기능이나 데이터 코드가 중복

중복의 제거

긴 메소드

메소드의 내부가 너무 김

메소드를 적정 수준의 크기로 나눔

큰 클래스

한 클래스에 너무 많은 속성과 메소드가 존재

클래스 몸집을 줄임

너무 많은 인수

메소드의 파라미터 개수가 너무 많음

파라미터의 개수를 줄임

두 가지 이상의 이유로 수정되는 클래스

(Divergent Class)

한 클래스의 메소드가 2가지 이상의 이유로 수정되면, 그 클래스는 한가지 종류의 책임만을 수행하는 것이 아님

한 가지 이유만으로 수정되도록 변경

여러 클래스를 통시에 수정 (Shotgun Surgery)

특정 클래스를 수정하면 그때마다 관련된 여러 클래스들 내에서 자잘한 변경이 필요

여러 클래스에 흩어진 유사한 기능을 한곳에 모음

다른 클래스를 지나치게 애용 (Feature Envy)

번번히 다른 클래스로부터 데이터를 얻어와서 기능을 수행

메소드를 그들이 애용하는 데이터가 있는 클래스로 옮김

유사 데이터들의 그룹 중복(Data Clumps)

3개 이상의 데이터 항목이 여러 곳에 중복되어 나타남

해당 데이터들을 독립된 클래스로 정의

기본 데이터 타입 선호 (Primitive Obsession)

객체 형태의 그룹을 만들지 않고, 기본 데이터 타입만 사용

같은 작업을 수행하는 기본 데이터의 그룹을 별도의 클래스로 만듦

 

3. 리팩토링의 목적 및 주요기법

  가. 리팩토링의 개념도

 

  나. 리팩토링의 목적

  • 버그식별의 용이성: 디버그는 아니나 숨은 버그 식별 용이
  • 기능추가의 용이성: 복잡하고 지저분한 코드 제거
  • 리뷰의 간편성: 코드가 읽기 쉽게 어 리뷰가 용이해짐
  • SW 안정성 확보: 리팩토링 통해 안정성 높은 SW 실현
  • SW 신뢰성 확보: 리팩토링 통해 신뢰성 높은 SW 실현
  • SW 재사용성 증가: 리팩토링 통해 안정성, 신뢰성 확보통해 재사용성 증가

 

  다. 리팩토링 주요기법

기법

상세 설명

Extract Method

- 그룹으로 함께 묶을 수 있는 코드 조각기 있으면 코드의 목적이 잘 드러나도록 메소드의 이름을 지어 별도의 메소드를 추출

Replace Temp With Query

- 어떤 수식의 결과값을 저장하기 위해서 임시 변수를 사용하고 있다면, 수식을 추출해서 메소드를 만들고, 임시 변수를 참조하는 곳을 찾아 모두 메소드 호출로 교체

Move Method

- 메소드가 자신이 정의된 클래스보다 다른 클래스의 기능을 더 많이 사용하고 있다면, 이 메소드를 가장 많이 사용하고 있는 클래스에 비슷한 몸체를 가진 새로운 메소드 생성

Extract Class

- 두 개의 클래스가 해야 할 일을 하나의 클래스가 하고 있는 경우 새로운 클래스를 만들어 관련 있는 필드와 메소드를 예전 클래스에서 새로운 클래스로 이동

Rename Method

- 메소드의 이름이 그 목적을 드러내지 못하고 있다면 메소드의 이름 변경

Substitute Algorithm

- 알고리즘을 보다 명확한 것으로 바꾸고 싶을 는 메소드의 몸체를 새로운 알고리즘으로 교체

Inline Method

- 메소드 몸체가 메소드의 이름 만큼이나 명확할 때는 호출하는 곳에 메소드의 몸체를 넣고 메소드를 삭제

 

  라. Refactoring고려사항

  • 리팩토링 수행 전, 프로그램의 동작이 변하지 않았음을 확인하기 위한 유닛테스트 준비
  • 리팩토링 자체가 디버깅이나 기능추가구현이 아니므로 완성되지 않은 코드 또는 버그가 많은 코드에는 적용 불가
  • 초기 설계를 조금씩 현실에 맞게 개선해 나가는 리펙토링 기법은 XP(eXtream Programming)에 적절
  • 비즈니스에 대한 이해가 우선시 되야 하며 refactoring 과정에서 실패 시 백업 및 복구대책 중요
  • 리팩토링 지원도구의 적절한 활용이 필요하지만 무엇보다 전문가의 안정적인 설계와 code inspection 능력 필요

 

4. 코딩오류 vs 코드스멜, 리팩토링 vs 리엔지니어링

  가. 코딩 오류 vs 코드 스멜

항목

코딩 오류

코드 스멜

개 념

설계 명세상의 누락이나

프로그래밍 기법상의 결함

프로그램 가독성이 나쁘고,

코드품질을 저하시키는 요인

프로그램 동작여부

동작하지 못함

동작

원 인

프로그래밍 기법상 결함,

사용자 요구 누락

프로그래머 능력

제거방법

리팩토링

리팩토링

 

  나. 리팩토링 vs 리엔지니어링

항목

Refactoring

ReEngineering

관점

모듈의 정제

현 시스템을 개선하는 접근

적용 범위

클래스, 모듈단위, 소단위

시스템등 대단위

반복성

반복 많음

반복이 거의 없음

적용예

특정 메소드 다형성 적용

시스템 전반적 모듈화

댓글