소프트웨어 개발 프로세스

|

소프트웨어 개발 이론에 대한 아주 유명한 그림이 있습니다.

image

고객이 원하는 것을 분석하고 제공하는 것은 정말 어려운 일입니다. 게다가 고객이 원하는 것은 시간이 지날수록 바뀌기도 합니다. 고객의 요구사항을 분석하고 그리고 변화해가는 요구사항에 대응해나가기 위해서 과거부터 다양한 소프트웨어 개발 프로세스들이 존재해왔습니다.


폭포수 모델(Waterfall Model)

image

위에서 아래로 물이 떨어지는 것처럼,

폭포수 모델 단계

  • 요구 사항 분석
  • 소프트웨어 설계
  • 소프트웨어 구현
  • 테스트 및 디버깅
  • 배포
  • 유지 보수

의 단계를 순차적으로 수행하는 모델입니다.

단계별로 구분이 뚜렷해서 이해하기 쉬운 모델이지만, 너무 이상적인 상황을 가정한 모델이기도 하고 개발 도중에 발생하는 여러 가지 상황들과 고객의 요구사항 변화에 대응하기가 너무 힘들다는 단점이 존재합니다.  (하지만 일반 회사의 윗 사람들은 좋아하는 방식이기도 합니다. 다른 모델에 비해 일정 관리가 수월하기 때문이죠.)


나선형 모델(Spiral Model)

image

선형 모델 단계

  • 목표 설정
  • 위험 분석(Risk 분석, 프로토타입)
  • 엔지니어링(요구사항 분석,점 설계, 구현, 테스트)
  • 다음 단계 계획[/su_box]

나선형 모델은 위의 4단계를 반복하는 프로세스입니다. 폭포수 모델에 위험 분석 단계를 추가하고, 각 단계를 반복 수행하면서 프로젝트의 리스크를 최소화할 수 있는 모델로, 대규모 프로젝트에 적합하다고 알려져 있습니다.

다만 프로젝트가 요구하는 과정이 많아져서 프로젝트 일정이 길어질 수 있으며, 성공 사례가 드물다는 단점이 있습니다.


애자일(Agile)

image

나선형 모델과 비슷하게 일정한 주기를 가지고 끊임없이 프로토타입(Prototype)을 만들고 그 때마다 요구사항을 체크해나가면서 프로젝트를 진행해나가는 방법입니다.

나선형 모델과 애자일의 목적

나선형 모델 목적 : 위험도 최소화
애자일 : 끊임없이 변화하는 고객의 요구사항에 유연하게 대처하는 것[/su_box]

그러기 위해서 애자일에서는 고객과 팀원간 꾸준한 교류를 하고 개발에 불필요한 문서 등은 최소화(Document-Oriented가 아닌 Code-Oriented)하고 팀원간 협업을 강조하고 있습니다. 민첩함을 높이기 위해 소규모의 팀을 선호하는 경향이 있습니다.

스크럼(Scrum)

30일마다 프로토타입을 만드는 애자일 방법론.
개발 주기를 30일로 잡고, 이를 스프린터(Sprint) 또는 이터레이션(Iteration)이라고 부른다.
매일 15분씩 회의를 하며 이슈나 우선사항을 서로 공유한다.

XP(eXtreme Programming)

의사소통, 피드백, 단순함, 용기, 존중에 가치를 두고 있는 방법론.
고객과 2주 정도의 반복 개발을 하며 테스트를 무척 강조한다.


애자일도 다양한 단점을 갖고 있습니다. 문서 중심이 아니다보니 설계 문서 등이 작성되지 않아 지원성(Supportability) 문제가 발생할 수 있으며, 멤버들의 기억력에 의존하는 경향이 있어 시간이 지나면 요구사항의 필요성, 설계의 방향 등에 대한 세부사항을 잊는 경우도 발생할 수 있습니다. 컴포넌트를 유지보수하기보다는 처음부터 다시 작성하는 경향을 갖게 될 수 있으며 이를 NIH(Not implemented Here) 증후군이라고 부릅니다.

불필요한 것들을 많이 줄이고 없애다보니 인프라 구조 등을 버리게 되는 상황도 발생할 수 있으며, 이는 대규모 프로젝트에 적합하지 않은 상황을 만들기도 합니다. 팀간 인터페이스 공유가 제대로 되지 않아 인터페이스 수정이나 리팩토링(Refactoring)에 어려움을 겪기도 하는 단점이 있습니다.

객체지향 설계 원칙 - SOLID

|

객체 지향 설계에서 유명한 5가지 원칙이 있습니다. 그 5가지 원칙의 첫 글자를 따서 ‘SOLID’ 원칙이라고 합니다. 굳이 외울 필요는 없지만 알아두면 나쁘지 않을 것 같습니다.


The Single Responsibility Principle (SRP)

각각의 클래스는 단 하나의 책임만 져야 한다.

예를 들어 다음과 같은 클래스는 너무 많은 일을 하고 있습니다.

image

이럴 경우, 위 기능 중 하나의 기능만 수정이 가해져도 클래스 내부 수정이 복잡하게 이루어질 가능성이 높습니다. 따라서 각 기능들을 분리하여 각각의 클래스로 만드는 것이 바람직합니다.


The Open-Closed Principle (OCP)

확장은 유연하게(Open), 수정은 폐쇄적(Close)이어야 한다.

아래와 같은 구조는 Employee가 EmployeeDB를, EmployeeDB가 Database를 바라보고 있습니다.

image

이 경우에는 Database 쪽에 수정이 발생하면 EmployeeDB 및 Employee 모두 수정이 발생하게 됩니다. 그래서 아래와 같은 구조로 바꾸는게 효율적입니다.

image

OCP를 잘 고려한 대표적인 디자인 패턴으로 ‘MVC(Model-View-Controller) 패턴’이 있습니다.


Liskov Substitution Principle (LSP)

서브 타입은 항상 자신의 Base Type으로 교체 가능해야 한다.

분명히 부모, 자식간의 관계가 될 수 없음에도 일부의 기능들이 비슷해보여서 부모, 자식 관계를 억지로 만드는 경우가 종종 있습니다. 이럴 경우 if 또는 instanceof가 남발이 된 코드가 만들어질 수 있습니다.

예를 들어,

public class Duck {

  public void fly();
  // ...
}

라는 클래스가 있고,

public class YellowDuck extends Duck {
  // ...
}

public class RubberDuck extends Duck {
  // ...
}

라는 클래스가 있을 때, YellowDuck은 문제없이 부모 Duck의 fly() 함수를 수행할 수 있지만, RubberDuck은 고무로 만든 오리이기 때문에 날지를 못합니다.

물론 RubberDuck 클래스의 fly() 함수 내부를 비워 두거나, Exception을 발생시키는 작업 등을 할 수는 있지만, 그에 따른 예외 처리나 경우의 수를 처리하다보면 결국 여기 저기에 if 또는 instanceof를 넣어서 다양한 분기에 대한 처리를 해줘야 하는 상황이 발생합니다.

결국 LSP도 OCP도 지키지 못하는 상황이 되어버렸습니다. 즉, 상속은 정확히 부모와 자식간의 관계가 될 수있는 경우에만 이루어져야 합니다.

이게 말은 쉽지만, LSP를 지키는 것인지 어긴 것인지 판단하기 어려울 경우가 많이 있습니다. 또한 실수를 하기도 쉽습니다. 위의 오리 클래스로 든 예는 좀 쉬운 예가 되지만, 아래와 같은 예제는 조금 더 실수하기 쉬운 경우입니다.

- 정사각형은 사각형이다.
- 정사각형은 사각형 중 4변의 길이가 모두 똑같은 경우에 해당한다.
- 즉, 정사각형 클래스는 사각형 클래스를 상속받아서 구현할 수 있다.

대부분의 경우는 위의 단계로 생각해서 처리하면 됩니다. 하지만 이게 모든 상황에서 적용될 수 있는 일반적인 상황일까요?

사각형 클래스인 Rectangle을 만들도록 하겠습니다. 그리고, 너비와 높이를 입력하거나 가져올 수 있는 함수를 만들고, 또한 사각형의 넓이를 리턴하는 함수까지 만들어 보겠습니다.

public class Rectangle {

  private int width;
  private int height;

  public int getWidth() {
    return width;
  }

  public void setWidth(int width) {
    this.width = width;
  }

  public int getHeight() {
    return height;
  }

  public void setHeight(int height) {
    this.height = height;
  }

  public int getArea() {
    return this.width * this.height;
  }
}

자, 여기까지는 특별히 이상한 점이 없습니다. 그러면 이 클래스를 상속받는 정사각형 클래스 Square를 만들어보겠습니다. 정사각형은 가로와 세로의 길이가 같기 때문에 클래스 내부를 다음과 같이 구현할 수 있습니다.

public class Square extends Rectangle {

  @Override
  public void setWidth(double width) {
    this.width = width;
    this.height = width;
  }

  @Override
  public void setHeight(double height) {
    this.height = height;
    this.width = height;
  }
}

과연 이렇게 하면 문제가 없을까요? 일반적인 생각이라면 큰 문제가 없을 것 같습니다. 하지만, 다음과 같은 코드를 살펴봅시다.

public class Test {

  public void check() {
    Rectangle rec = new Square();
    rec.setWidth(4);
    rec.setHeight(5);

    System.out.println("Area : " + rec.getArea());

  }
}

자, getArea()의 결과로 무엇이 나와야 할까요? rec 인스턴스의 너비는 4, 높이는 5로 세팅을 했습니다. rec는 사각형의 속성을 갖고 있기 때문에 넓이는 ‘4 x 5 = 20’이 나와야 정상입니다. 하지만 위 코드는 기대한 값이 나오질 않습니다. 즉, 잘못된 상속으로 인해 문제가 발생하는 경우입니다.

LSP는 처음부터 예측하기가 어렵습니다. 프로젝트 초반에는 주어진 조건에서 LSP를 어기는 경우가 발생하지 않았지만, 프로젝트가 커지면서 후반에 LSP를 어기는 경우가 발생하기도 합니다. 그래서 상속은 최대한 신중하게 결정해야 합니다.


Dependency Inversion Principle (DIP)

의존 관계 역전 원칙. 추상(Abstract) 클래스는 구체화(Concrete) 클래스에 의존성을 가지면 안된다.

너무 당연한 이야기입니다. 구현화 레벨에 해당되는 Concrete 클래스는 프로젝트를 진행하면서 수시로 바뀔 수가 있습니다. 수정 사항이 발생하더라도 추상 클래스에 영향을 주지 않기 위해서는 추상 클래스가 Concrete 클래스에 의존성을 가지면 안됩니다.


Interface Segregation Principle (ISP)

인터페이스 분리 원칙. 각 클래스는 자신이 사용하지 않는 인터페이스에 의존성을 가지면 안된다.

앞서 말한 SRP와 비슷한 이유입니다. 실제 사용하지도 않는 인터페이스들에 대한 의존성을 너무 많이 가질 경우, 향후 그 인터페이스들의 변경이 발생할 때 각 클래스에도 변경이 발생하게 됩니다. 따라서 꼭 필요한 인터페이스에만 의존성을 가져야 향후 유지 보수가 간편해집니다.

Authentication과 Authorization 차이

|

Authentication과 Authorization를 많이 섞어서 쓰거나 혼동해서 쓰는 경우가 많은데, 이 둘의 차이가 무엇인지 포스팅해보도록 하겠습니다.

간단히 한 단어로 각각을 설명하면 다음과 같습니다.

Authentication : 인증
Authorization : 인가

좀 더 설명을 해보면, Authentication은 로그인(Login)과 같이 자신을 인증하는 것이고, Authorization은 로그인한 그 사람이 관리자(Admin)인지 일반 사용자인지, 게스트인지 구분하는 것이라고 생각할 수 있습니다.

그리고 추가적으로 Certification은 특정 기관의 인증, 심사를 통과한 인증을 말합니다.

Windows 10 - Bash 설치

|

Windows 10에서 Bash를 설치하는 방법

2016년 봄부터 윈도우 10에서 Bash를 사용할 수 있도록 제공하고 있습니다. 하지만, 아직 정식 지원은 아니며 개발자 모드를 활성화해야만 사용할 수 있습니다. 더 자세한 내용은 여기에서 확인할 수 있습니다.


Bash를 사용하기 위한 최소 사양

  • Build 14316 이후
  • 64 bit 운영체제

시스템 정보에서 내 PC가 요구 사양을 만족하는지 확인 할 수 있습니다.

image


설치 방법

먼저 개발자 모드를 설정합니다.

모든 설정 → 업데이트 및 복구 → 개발자용

image

Windows 기능 켜기/끄기에서 ‘Linux용 Windows 하위 시스템(베타)’를 설치합니다.

제어판 → 프로그램 제거 및 변경

image

image

명령 프롬프트 창에서 bash를 실행합니다.

image

이제 bash가 설치 완료되었습니다.

Eclipse 실행 할 때 Failed to create the Java Virtual Machine 오류 뜰 때

|

이제는 Android Studio 만을 사용하고 있어서 거의 필요가 없는 글일 수도 있지만, 가끔씩 Eclipse를 사용해야 할 경우도 있어서(과거에 작성한 프로젝트를 수행한다거나) 포스팅을 해봅니다. Eclipse를 사용하다가 가끔씩 Failed to create the Java Virtual Machine 오류가 뜨는 경우가 있습니다.

이런 경우는 Eclipse가 있는 폴더에 가서 eclipse.ini 파일을 수정해주면 됩니다. (하지만, 역시 Eclipse에서 Android Studio로 갈아타는게 제일 좋은거 같습니다.)


수정 전 eclipse.ini

-startup
plugins/org.eclipse.equinox.launcher_1.3.0.v20120522-1813.jar
--launcher.library
plugins/org.eclipse.equinox.launcher.win32.win32.x86_1.1.200.v20120522-1813
-product
com.android.ide.eclipse.adt.package.product
--launcher.XXMaxPermSize
256M
-showsplash
com.android.ide.eclipse.adt.package.product
--launcher.XXMaxPermSize
256m
--launcher.defaultAction
openFile
-vmargs
-Dosgi.requiredJavaVersion=1.6
-Xms40m
-Xmx768m
-Declipse.buildId=v21.0.1-543035


수정 후 eclipse.ini

-startup

plugins/org.eclipse.equinox.launcher_1.3.0.v20120522-1813.jar
--launcher.library
plugins/org.eclipse.equinox.launcher.win32.win32.x86_1.1.200.v20120522-1813
-product
com.android.ide.eclipse.adt.package.product
--launcher.XXMaxPermSize
128M
-showsplash
com.android.ide.eclipse.adt.package.product
--launcher.XXMaxPermSize
128m
--launcher.defaultAction
openFile
-vmargs
-Dosgi.requiredJavaVersion=1.6
-Xms40m
-Xmx768m
-Declipse.buildId=v21.0.1-543035