블로그 이미지
소프트웨어 개발 경험을 공유하고 싶은 재밌게 사는 소프트웨어 엔지니어입니다^^

카테고리

Chungha Story (41)
Agile Experience (22)
My Family (0)
Life Style (7)
Programming (8)
Android (2)
Total
Today
Yesterday

달력

« » 2024.3
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
31

최근에 올라온 글

최근에 달린 댓글

저희 회사에서 제품의 품질을 높이기 위해 지난 1년간 코드리뷰를 전사적으로 진행했습니다.
하지만 리포트 형식만 던져주고 어떠한 교육이나 코칭이 없는 상태에서 진행되다 보니 업무에 부담만 되었습니다.
소스코드 출력해서 수정된 부분 체크하고 이전 버전도 체크하고..등등..ㅠㅠ

몇달전에 박형근님 블로그(코드리뷰, 좀 쉬운 방법은 없을까?)에서 공짜로 책을 준다는 글을 읽고 바로 신청했습니다.
지금도 공짜로 보내주고 있네요^^

책을 읽어보니 이메일로 코드리뷰를 하는 방법이 소개되어 있었습니다. 그래서 가장 간단한 형태로 적용해보기로 했습니다
그 형태는 체크아웃된 소스코드를 작업하고 체크인할 때 수정내용을 입력하도록 하고 이전 버전과의 차이점을 이쁘게!!! 팀원 전체에게 이메일로 보내는 것이었습니다. 이렇게 하면
  1. 팀원들이 쉽게 볼수 있기 때문에 코드 정리에 더 신경쓰게 된다.
  2. 어떤 내용을 수정했는지 히스토리가 명확하게 남을 수 있다. (모두가 보고 있음..ㅋㅋ)
살짝 이 정도의 장점이 생기는것 같습니다.
물론 반발을 최소화 하기 위해 저 방법을 안써도 체크인 할 수 있도록 했습니다. 
하지만 요즘은 팀장님이 리뷰할 수 있게 체크인 해달라고 요구하십니다.ㅋㅋ 팀장님은 상시 리뷰어로써의 역활을 하고 계시죠..
굉장히 단순한 형태이긴 하지만 그 효과는 괜찮은것 같습니다.

저희 코드리뷰 이메일은 다음과 같이 vim에서 보는 것과 동일한 형태로 보내집니다.
  • vim에서 C syntax highlight에 diff highlight를 추가하였습니다.
  • vim에서 :runtime! syntax/2html.vim 을 실행하면 highlight 된 형태를 html 포멧으로 저장해줍니다.
Posted by 윤청하
, |
낡은 코드(legacy code)에 관심을 갖다

저는 최근에 낡은 코드에 관심을 갖게 되었습니다. 낡은 코드에서 효과적으로 일하기 위해서는 테스트를 만드는 것이 가장 먼저 해야 할 일입니다. 테스트가 없다면 어떤 코드도 쉽게 수정하기 어려울 것입니다. 그런데 막상 테스트를 만들려고 해보니 막막했습니다. 어디서 부터 테스트 해야 할까?

낡은 코드는 큰 것부터 테스트 하자

처음에는 막막했습니다. 뭐부터 해야할 지.. 함수단위, 라이브러리단위로 테스트 하자니 그 양이 엄청 났습니다. 또한 테스트 하기 쉽게 작성된 코드들이 아니기 때문에 더욱 어려웠습니다. 저는 블로그를 통해서 조언을 얻었습니다.(ParkPD님, 박형근님 감사합니다.) 그 첫번째가 "큰 것 부터 하라"였습니다. 그래서 먼저시스템 테스트를 만들기로 하였습니다.
다른 팀원들이 테스트를 쉽게 만들수 있게 도와주자

저는 낡은 시스템의 테스트 케이스를 검토하고 아래 그림과 같은 상상을 했습니다. 저희 회사 제품의 시스템 테스트 케이스는 시간적인 순서를 갖는 제어 메시지를 주고 받는 형태로 작성 될 수 있습니다. 저는 이것을 시나리오라고 하고 제어 메시지에 내용은 제어정보라고 정의하였습니다. 제어정보와 시나리오를 처리하는 것은 mock 클라이언트라고 정의하였습니다.

내가 상상한 시스템 테스트

개발자 혹은 테스터는 간단한 스크립트언어로 시나리오와 제어정보를 작성합니다. 독립적으로 작성된 시나리오와 제어정보를 조합하여 많은 테스트 케이스를 만들수 있습니다. 이런 것을 팀원들에게 제공한다면 테스트 케이스 작성에 큰 도움이 될 것 같았습니다. (저희 팀에는 유지보수 중인 낡은 시스템이 많습니다.^^;;)

의존성 주입 (Dependency Injection)

의존성 주입이란 객체간의 의존성을 미리 코드에 정의하는 것이 아니라 실행 시간에 주입하는 것을 의미합니다. 의존성 주입은 제어 역전(Inversion of Control)이라고도 합니다. 즉, 제어를 역전시킴으로써 바이너리를 교체하지 않고 제어를 가능하게 하는 것입니다. 이 개념은 자바의 Spring이라는 프레임워크에서 핵심 기능으로 사용되고 있습니다.
C++에서 의존성 주입을 하기 위해서는 작성된 라이브러리의 API를 동적으로 호출하고 파라미터를 실행시간에 입력하는 코드(wrapper code)를 생성해야 합니다. 즉, 이 코드(wrapper code)는 텍스트 파일에 기술된 100이라는 문자열을 정수형 변수에 넣을 수 있어야 합니다.

시스템 테스트 프레임워크

저는 절친 팀원 한분과 시스템 테스트를 위한 mock 클라이언트 작성을 도와주는 프레임워크를 만들기로 하였습니다. 물론 팀장님이 인가해주셨습니다. (매출과 관계없는 일을 싫어하는 회사 특성상 이례적인 경우일수 있습니다.) 위의 그림을 바탕으로 여러가지 시도를 해봤습니다.
  • 시도1) 시나리오 및 제어정보 스크립트 인터프리터 + 의존성 주입용 코드 생성기 + 스크립트 생성기
    시나리오와 제어정보 스크립트 언어를 정의하고 Lex와 Yacc를 이용하여 인터프리터를 만들었습니다. 의존성 주입을 위해 gccxml을 이용하여 API를 XML로 만들고 그것을 분석하여 코드 생성기를 만들었습니다. 스크립트 작성을 용이하게 할수 있도록 도와주는 스크립트 생성기도 작성하였습니다.
    문제는 의존성 주입용 코드 생성기였습니다. 생각보다 예외 상황이 많았습니다. 그리고 우리가 정의한 스크립트 언어는 점점 복잡해졌습니다. 패스~

  • 시도2) Python + Swig
    반성합니다. 시도1을 할때 충분히 조사하지 못했습니다.
    Swig는 Python과 C++을 연결시켜주는 코드 생성 라이브러리입니다. 매우 훌륭합니다.
    그러나, Python에서 API를 호출하는 것은 쉬웠지만 거꾸로 이벤트를 알려줄때 C++의 데이터가 Python 데이터로 맵핑 되는 작업이 매우 난해했습니다. 패스~

  • 시도3) 시나리오 작성용 인터페이스 및 시나리오 실행 환경 + 의존성 주입 라이브러리
    욕심을 버렸습니다. 시나리오는 C++로 작성하기로 하였습니다. 시나리오간에 의존성을 줄이고 이벤트를 알려줄 수 있는 인터페이스를 정의하였습니다. 해당 인터페이스를 구현하여(shared object or DLL) 시스템 프레임워크 월드에 주입하면 자동으로 해당 시나리오가 실행되도록 하였습니다. 제어정보와 시나리오 정보는 의존성 주입 라이브러리(Autumn Framework)를 사용하였습니다. 전체적으로 매우 심플하고 다양한 시나리오(기능 시험, 성능시험 등)를 XML로 정의할 수 있게 되었습니다. 콜!

    "시도3"의 전체 구성 그림

현재 저희는 시나리오 인터페이스를 구현하여 여러가지 테스트 케이스를 쌓고 있습니다^---^ 저희가 사용하는 인터페이스는 아래와 같습니다. 매우 간단합니다. 이 인터페이스를 구현하고 XML을 정의하면 자동으로 테스트 됩니다.
이제 남은 숙제는 지속적 통합을 위한 서버 구축(CI)입니다. C++과 리눅스 환경에서 효과적인 CI 서버 구축을 위한 방법을 알고 계신분은 조언부탁드립니다.^^ 감사합니다.

'Programming' 카테고리의 다른 글

이메일로 코드 리뷰 하기  (12) 2010.02.26
CASE STUDY : 의존성을 역전 시키자  (10) 2009.12.02
Broken Window Theory and Software  (4) 2009.11.26
Posted by 윤청하
, |
새로운 업무를 받음

얼마전 오래된 시스템(legacy system)에 새로운 플랫폼의 부가(extension) 모듈을 이식하라는 업무를 할당 받았습니다. 쉽게 생각하면 새로운 플랫폼은 유연성을 크게 가질수 있도록 작성되었으니 쉽게 이식 될 수 있다고 오해 할 수 있습니다. 저는 먼저 오래된 시스템과 새로운 플랫폼의 의존성을 확인하였습니다. 오래된 시스템의 일부 코드를 수정(modification)하는 것은 일단 허용하기로 하였습니다. 하지만 새로운 플랫폼의 부가 모듈을 바로(쉽고 빠르게 수정없이) 이식하기에는 문제가 있었습니다. 

무엇이 문제였을까

새로운 플랫폼의 부가 모듈은 하위 모듈(base library)에 의존성(dependency)을 가지고 있었습니다. 일반적으로 계층 기반(layer based)으로 모듈을 작성할 때 하위 계층에서 상위 계층을 직접 참조하는 것은 원칙적으로 불허 하지만, 그 반대의 경우는 허 하는 경우가 많습니다. 간단히 생각해보면 이는 크게 문제가 되지 않을 수 있지만, 상위 모듈이 하위 모듈에 의존하기 때문에 상위 모듈의 유연성, 재사용성은 매우 떨어지게 됩니다.

해결 방법

이와 같은 상황을 해결하는 방법은 다음과 같습니다.
  1. 오래된 시스템에 하위 계층까지 모두 이식한다.
  2. 새로운 플랫폼의 부가모듈을 컴파일 옵션으로 시스템에 따라 다른 하위 레이어를 사용하도록 수정한다.
  3. 새로운 플랫폼의 부가모듈을 복사하여 오래된 시스템에 맞도록 수정한다.
  4. 새로운 플랫품의 부가모듈이 가지는 하위 계층에 대한 의존성을 제거한다.

의존성을 역전시키자

위의 문제에서 하위 계층에 대한 의존성을 제거하는 것(해결방법 4)을 객체지향 디자인에서는 의존성 역전 원칙(The Dependency Inversion Principal)이라고 합니다.
High level modules should not depend upon low level modules. Both should depend upon abstractions.
Abstractions should not depend upon details. Details should depend upon abstractions.
이 원칙은 상위모듈은 하위 모듈의 추상 클래스(또는 인터페이스)에만 의존함으로써 구체적인 구현에 대한 의존성은 갖지 않도록 하는 것입니다. 
위의 상황에서 의존성 문제가 되는 하위 모듈 중의 하나는 타이머입니다. 만약에 해결방법 1로 해결한다면 다음과 같은 그림처럼 작업이 이루어질 것입니다. 즉, 오래된 시스템에도 TimerB가 존재하지만, TimerA까지 이식함으로써 부가 모듈을 이식 가능하게 합니다. 이는 타이머 기능의 중복을 만들게 됩니다. 때에 따라서 TimerA의 의존성 때문에 다른 모듈들이 줄줄이 딸려가는 일이 생길수도 있습니다.
해결방법 2로 하게 되면 모듈의 구현 복잡도는 증가하게 될 것이며, 해결방법 3은 새로운 소스코드 브랜치가 생기게 되어 관리에 대한 부담이 증가하게 됩니다.
마지막으로 해결방법 4로 이 문제를 해결하면 다음과 같은 그림처럼 작업이 이루어 질 것입니다.
즉, 부가모듈은 ITimer라는 인터페이스에만 의존하게 되고 새로운 플랫폼과 오래된 시스템은 각각 해당 인터페이스를 Adapter Pattern을 이용하여 TimerA, TimerB로 구현하면 됩니다. 결과적으로 부가 모듈만 깔끔하게 이식할 수 있습니다.
저는 이 문제를 해결방법 4를 이용하여 해결했습니다. 제가 정의한 추상클래스는 다음과 같습니다.
IMpTimerListener는 타이머의 이벤트를 받기 위한 추상클래스이며, IMpTimer는 타이머 등록 및 해제를 위한 추상클래스입니다.

회고(retrospective)

사실, 이 업무를 진행하면서 모든 의존성을 위와 같은 방법으로 해결하지는 못했습니다. 기존 구현에 따라 해결방법 2를 사용한 경우도 있었습니다. (Define으로 구현되어 있어서 불가피 했음) 하지만 이러한 리팩토링을 통해 위의 부가모듈의 의존성은 상당히 사라지게 되었고 재사용성은 크게 증가하게 되었습니다. (추후 재사용 요구사항이 예상되는 모듈이었습니다.) 모든 모듈에 대해서 재사용성에 포커스를 두는 것은 over-engineering의 가능성이 있습니다. 하지만 이와 같이 재사용 이슈가 예상되는 모듈에 대해서는 의존성을 철저히 관리함으로써 추후 유지보수 업무를 줄일 수 있을 것입니다.^^ 감사합니다.

Posted by 윤청하
, |
오늘 저녁에 얼마전 취직한 후배가 저녁을 사달라고 해서 만났다. 영동시장에서 맛있는 차돌백이 + 숙주나물을 먹고 나와서 강남대로에 있는 커피빈에 갔다. 요즘들어 술마시는 것보다 커피마시면서 조용히 수다떠는것에 더 익숙해졌다.^^ (아직 30대도 안되었는데...ㅠㅠ)

한참을 떠들고 나오다가 커피빈 화장실에 들렸다. 세면대에서 손을 씻고 페이퍼타올을 뽑아서 손을 닦고 버릴려고 하는데.. 분명히 바닥에 휴지통이 있음에도 불구하고 세면대 옆에 지져분하게 다쓴 페이퍼타올들이 수북히 쌓여있었다.

분명 처음부터 그런 상황은 아니었을 것이다. 또한 커피빈같은 점포 특성상 일정한 주기로 화장실을 청소하기 때문에 매우 오랬동안 방치되지 않았을 것이라 추측한다. 하지만 분명히 20명에 가까운 사람이 쓴듯한 페이퍼타올이 수북히 쌓여있었다. 왜그럴까?

문득 예전에 읽은 "실용주의 프로그래머"라는 책에서 읽은 "깨진 유리창 이론(broken window theory)"이 생각났다. 깨진 유리창 이론은 사소한 문제를 해결하지 않았을 경우 본격적인 문제로 발전할 수 있다는 내용이다. 커피빈의 화장실 사건은 아마 어느 누군가의 사소한 청결에 대한 무관심에서 시작되었을 것이다. 버려진 하나의 휴지는 다른 이들에게 세면대 옆에 휴지를 버릴수있도록 허용한 것이다.

소프트웨어 개발에서 깨진 유리창을 방지하는 것은 매우 중요하다. 한눈 팔다보면 이미 소프트웨어는 돌이킬수 없는 상태로 복잡하고 지져분하게 될수 있으며, 리팩토링을 위해서 매우 큰 비용을 부담해야 할수 있다. 사수가 만들어 놓은 깨진 유리창이 있는 코드에 후임이 불을 질러 놓았다고 해서 후임을 비난할 수는 없을 것이다. 소프트웨어 개발자는 정말 부지런해야 한다. 항상 자신의 코드에 깨진 유리창이 없는지 확인해야 한다. 켄트백은 말단연구원도 책임감(responsibility)을 위에서 끌어와서 자신을 다치기 쉬운 상태로 놓는것이 바람직하다고 했으며 사람다운 개발자가 되기 위해 필요하다고 했다. (매우 역설적으로 들릴수 있다.)

아주 바쁘다 보면 빠르고 쉽게 생성되는 코드들이 있다. 이들이 문제인 경우가 많다.^^;; 특히 BMT...
우리에게 책임감이 있다면 BMT 같은 상황에서 생성된 깨진 유리창들은 꼭 고쳐놔야 할 것이다.

'Programming' 카테고리의 다른 글

CASE STUDY : 의존성을 역전 시키자  (10) 2009.12.02
여러분의 소스코드는 안녕하십니까?  (7) 2009.11.24
개발언어와 개발스타일  (5) 2009.11.23
Posted by 윤청하
, |
10년 묵은 잘못된 소스코드 관리

어제 스프린트 미팅에서 있었던 일입니다. 스토리 중에 새로운 BMT에 대응하는 스토리가 있었습니다. 팀장님은 이전에 만들어진 제품에 새로 개발된 플래폼을 이식하여 해당 BMT에 대응하기를 원하셨고, 이를 위해 현재 플래폼 소스코드를 복사하여 새로운 저장소(SVN에서의 repository 또는 ClearCase에서의 Vob)를 만들라고 주문하셨습니다. 쉽게 말해서 중복된 소스코드 두개를 만드는 것입니다. 저희 회사는 제품의 특성상 시스템 장애에 굉장히 민감합니다. 따라서 어쩔수 없이 상용 사이트에 납품된 제품의 소스코드는 사이트 별로 따로 관리하고 있습니다. 하지만 진짜 문제는 복사되어 분리된 소스코드는 그 사이트에서 해당 제품을 폐기하기 전까지 유지된다는 것입니다. 즉, 사이트 별로 거의 비슷한 소스코드들이 생산되는 것입니다. 이는 다음과 같은 문제를 야기시키고 있습니다.
  1. 소스코드 관리 리소스 증가 : 새로운 기능이 개발되거나 버그가 발생했을 경우 매우 많은 저장소에 동일한 수정과 동일한 테스트를 해야합니다.
  2. 저장소별 히스토리 관리 : 저장소 별로 수정 히스토리를 따로 관리해야 합니다.
  3. 같은 문제가 번복됨 : 1번을 잘 못할경우(잘 못하고 있습니다.) 같은 버그가  여러 사이트에서 발생합니다.
즉, 유지보수에 굉장히 많은 리소스가 투입되며, 매출 증가 대비 필요한 리소스는 계속 증가하게 됩니다.
이는 10년동안 지속되온 문제입니다.ㅠㅠ

해결하기

다행히도 제가 생각하는 해결방법과 팀장님이 생각하는 해결방법은 동일했습니다. (팀장님은 이상적 방법이라는 표현을 쓰시긴했지만...) 해결 방법은 다음과 같습니다.
  1. 메인 소스코드를 버전별로 관리한다.
  2. 사이트 혹은 BMT에 대응하는 경우 현재 버전의 소스를 복사하여 작업한다. 단, 메인이 되는 소스코드와 동기화는 맞추어야 한다.
  3. 소스코드는 일정한 주기로 버전 업을 한다. (약 4주)
  4. 사이트에서 생성되는 요구사항은 일정한 주기로 버전 업 되는 메인 소스코드에 구현한다. 완료되고 적용될 때에는 기존의 복사된 소스코드를 삭제하고 새로운 버전의 소스코드를 복사하여 해당 사이트에 유지한다. (버전 업)
즉, 사이트별로 중복된 소스코드를 사용하지만, 메인 소스코드에 집중하고 잘 유지함으로써 지속적으로 동기화 될수 있는 방법입니다.

10년동안 바꾸지 못한 이유

위의 제시된 방법에서 큰 신뢰성이 요구되는 부분이 있습니다. 사이트의 소스코드를 버전업 하는 경우 기존 기능의 안정성 여부 및 타 시스템간의 호환성 유지 여부가 매우 중요하게 됩니다. 10년동안 바꾸지 못한 이유는 바로 이 점을 QA조직에서 개발팀을 신뢰하지 못하기 때문입니다.ㅠㅠ
이제 개발팀이 해야 할 일들이 눈에 보이기 시작합니다.
  1. 회귀테스트를 유지하고 관리해야 합니다. 자동화된다면 정말 좋을 겁니다. (TDD 강추..ㅠㅠ)
  2. 하위 버전 호환성 테스트를 유지하고 관리해야 합니다. API가 변경되지 않는다면 더욱 좋을 겁니다.
저는 이런 작업들이 이상적이라고 생각하지 않습니다. 당연히 개발자들이 해야 할 일들이라고 생각합니다. 물론 초반에 업무량은 더 많아 질수 있습니다. 하지만 가까운 미래에는 사람다운 개발자가 될수 있을거라 기대합니다^^

어제 회의에서 팀장님께 강력하게 요청했는데 어떻게 반응하실지는 두고봐야 겠네요..^^ 간만에 강하게 나가니까 좀 당황하신듯...ㅋㅋㅋ 10년동안 방치된 방법을 바꾸는게 쉽지는 않겠지만 이대로 간다면 살아남기 쉽지 않기 때문에 뭐라도 해야겠죠..^^

'Programming' 카테고리의 다른 글

Broken Window Theory and Software  (4) 2009.11.26
개발언어와 개발스타일  (5) 2009.11.23
Subtype Polymorphism과 성능 이슈  (4) 2009.11.17
Posted by 윤청하
, |
최근에 잘 나가는 회사의 팀장급 개발자 분이 하신 말씀이 생각 난다.
php를 좋아하면서 객체지향을 잘하는 사람을 본적이 없는데..ㅡㅡ^
그분의 말투는 마치 C style 개발자는 Loose Coupling, High Cohesion 과는 거리가 멀다는 것처럼 들렸다.
조금 비꼬아서 듣자면, C style 개발자는 스파게티 소스를 만들어낼 가능성이 크다는 것이었다.

내가 현재 재직중인 회사는 전형적인 C언어 개발 회사이다. 현재는 잘 모르겠지만 오래전(약 10년전)에 우리 회사에서 C언어로 만들어진 base library(자료구조, 로그, 통신등)들은 매우 훌륭했다. 어떤 도사분이신지는 모르겠지만 define으로 template까지 구현해놨다. 사실 현재 회사의 개발 능력으로 볼 때 그 base library의 출처가 의심스럽다.^^;; (깊이 알면 다친다..)
하지만, 솔직히 base library를 제외하고는 전체적으로 스파게티 소스에 가까운 경우가 많다. 물론 처음부터 스파게티 소스인 경우는 매우 드물다. 빈번한 customizing, 검증 받지 못한 코드의 커밋, 무분별한 BMT 성 코드 삽입등으로 인해 소스코드는 점점 스파게티처럼 꼬여만 간다.ㅠㅠ 이게 C로 개발하기 때문일까? (누가 답변좀~ㅋ)

나는 회사에서 객체지향으로의 패러다임 전환을 주장하고 있다. 하지만 꼭 C++을 쓰자는 것은 아니다.
단, 객체지향 언어에서 제공하는 것들을 잘 이용한다면 생산성에 더 도움이 될 수 있다에 한표를 던지고 싶다. (define문을 디버깅 한다고 생각해보면..끔찍하다)

나는 어떤 개발자의 주력 언어가 그 개발자의 개발 스타일을 어느정도 나타낼수 있지만 전부를 나타낼수는 없다고 생각한다. 그래도 패러다임이 다른 최소한 하나이상의 언어는 공부하는건 추천하고 싶다ㅋㅋ (요즘 python에 매료되었다는...ㅋㅋㅋ)

써놓고 보니..잡담이 되어버렸다는..ㄷㄷㄷ;; 
Posted by 윤청하
, |

객체지향 코덱 개발

제 업무의 60%는 표준 영상 코덱 개발입니다. 코덱개발은 성능과 품질이라는 trade-off를 가지고 있습니다.
더 많은 연산을 수행 할 수록 품질이 향상됩니다. 최근 멀티미디어 제품에서 품질 이슈가 굉장히 중요해졌습니다. 따라서 성능 개선이 가장 중요한 숙제중에 하나입니다. (대용량 미디어 처리를 위해 한 화면을 인코딩-디코딩하는데 약 2ms 이하로 유지해야 합니다.)
더불어 개발자에게 유지보수성(maintainability)은 버릴수 없는 가치 중에 하나입니다. 고객에게 직접적인 비즈니스 가치를 줄수는 없지만, 그 가치를 만들 수 있는 토대가 된다고 생각합니다.
기존의 영상 코덱은 주로 C언어로 개발되었습니다. 유지보수성은 철저하게 배재되었습니다. 자연히, 유지보수 가능 인력은 매우 제한되고, 기능 추가에 따른 위험은 매우 크며, 장애가 날 경우 원인 파악이 어려웠습니다. (대용량 미디어 처리 특성상 상용사이트에서 로그를 남기기 어렵습니다.)
그래서 저는 현재 기능 개발이 완료된 최신 영상 코덱(통신시장에서는 적용이 좀 늦습니다.)을 객체지향적으로 설계하고 개발하였습니다. (코덱의 복잡도가 매우 크게 증가해서 그랬습니다.ㅠㅠ 잘못한건가요??)

Subtype Polymorphism

Intellectual Wanderlust라는 블로그에 보면 "OOP란 조건문(if)을 줄이는 것"이라는 글에서 처럼 subtype polymorphism을 이용하여 조건문을 줄이는 것이 OOP의 핵심중 하나입니다. (IF문 안쓰기 운동도 있습니다. 껄껄^^ http://www.AntiIfCampaign.com 저도 동참하려고합니다(__ ))
보통 subtype polymorphism은 interface를 정의하는 base class를 상속(inheritance) 받아서 구현합니다.
따라서, virtual function을 자연스럽게 사용하게 됩니다.
(물론 C언어에서도 function pointer를 이용하여 비슷하게 구현할 수 있습니다. 좀 귀찮은 일이 많아 질 뿐이죠^^;;;)

Virtual Function과 성능 이슈

virtual function을 사용하게 되면 vtable이란 것이 생성됩니다. vtable을 이용하여 run-time에 어떤 함수를 호출할 건지 정하게 됩니다. 따라서, compile-time에서의 inline을 사용할 수 없습니다.(아픕니다.ㅠㅠ) 또한 vtable을 참조하여 함수호출이 이루어지는 경우 해당 주소로 redirect 해서 호출하게 되므로 성능이슈에 큰 영향을 줄 수 있습니다. 이러한 점들은 함수의 덩치가 크고 호출되는 횟수가 많지 않다면 전혀 문제가 될수 없지만, 영상 코덱의 경우 굉장히 호출횟수가 많은 작은 함수들이 다수 존재하기 때문에 문제가 됩니다.
또한 객체들의 메모리 사이즈가 virtual function의 갯수 곱하기 4bytes만큼 증가합니다.(32bit machine 기준)
이는 작은 internal memory(2MB under)에서 많은 작업을 해야 하는 DSP에서 제약이 될 수도 있습니다.

그럼 어떻게 해결할 수 있는가?

저는 호출횟수가 많지 않은 곳에서는 subtype polymorphism을 적용하고, 호출횟수가 많은 곳에서는 interface class에 기본적인 내용을 구현하였습니다. (다행히 단순한 기능들이라 가능했습니다^^;;;) 
결국 근본적인 해결이 아니기 때문에 부족한 부분이 많습니다. 그래서 찾아보고 고민하고 있습니다.!! (도움을 주세요~)


인터넷을 찾다가 virtual function을 template으로 대체 할 수 있다는 구문을 보고 찾아낸 방법입니다.
다음과 같은 방법으로 template을 사용하여 virtual function을 구현할 수 있습니다. 

결과는 child의 print()가 호출됩니다.
child = 123

약간(?)의 노력이 필요하지만 필요한 것을 모두 얻을수 있습니다. (base class가 복잡해 질듯..ㅋㅋ)
추후에 코드를 리팩토링 하면서 조금씩 적용해봐야 할 것 같습니다.^^
어디 더 좋은 방법 없나요?~~
Posted by 윤청하
, |
어제 회사에서 한분이 문자열 복사에 대한 질문을 하셨습니다. 
처리 결과를 문자열로 복사하여 리턴하는 부분의 코드였습니다.
그분의 코드를 리뷰하던 중에 다음과 같은 코드를 발견했습니다.
strncpy(rtString, "이건 이래서 실패했습니다.", sizeof(rtString));
해당 코드는 위험한 부분이 있습니다. 복사하고자 하는 문자열의 길이가 sizeof(rtString)의 크기보다 작다면 null 문자까지 정상적으로 복사되지만 그 크기를 넘어간다면 문제가 될 수 있습니다. 
당장은 buffer overflow가 발생하지 않지만, 추후 rtString을 출력하거나, 복사할때 큰 문제가 생길수 있습니다.

strncpy()의 man page를 보면 다음과 같이 기술하고 있습니다.
The strncpy() function is similar, except that not more than n bytes of src are copied. Thus, if there is no null byte among the first n bytes of src, the result will not be null-terminated.
이와같은 경우에는 snprintf() 쓰는 것이 더 안전합니다. 다음 예제를 보겠습니다.
위 코드의 실행 결과는 다음과 같습니다.
strncpy = 12345*****
snprintf = 1234

결과에서 snprintf()는 입력된 버퍼의 길이에 맞게 null 문자까지 포함하여 안전하게 복사해 주고 있습니다.
사실 매우 초보적인 내용일 수 있지만 잘 알지 못하고 쓰면 나중에 큰 화를 부를수도 있기에 정리해봤습니다^^

'Programming' 카테고리의 다른 글

여러분의 소스코드는 안녕하십니까?  (7) 2009.11.24
개발언어와 개발스타일  (5) 2009.11.23
Subtype Polymorphism과 성능 이슈  (4) 2009.11.17
Posted by 윤청하
, |