2014. 4. 24. 11:29

[AWS 시작하기]

Amozon Web Services (AWS)는 온라인 어플리케이션을구축할 수 있는 종량제(pay as you go) 컴퓨팅 자원 및 서비스를 제공한다. 예를 들어, 언제라도 접속하고 설정하고, 안전하며 원하는 시점에 실행할 수 있는 물리적 서버와  비슷한 서버를 AWS에서 임대할 수 있다. 직접 소유한 물리적 서버와 AWS의 가상 서버가 다른 점은 가상 서버가 행성 규모(planet-scale)의 네트워크 상에서 AWS에 의해 관리되고 있다는 점이다.


가상 서버는 실행되는 동안만 과금되며, 최초 구매 비용 발생하지 않고, 정기적으로 지불해야 하는 관리 비용도 없다. AWS 네트워크 상에서 운영되는 데다가, 물리적 서버로는 불가능한 어플리케이션 부하 증가 시에 필요에 따라 자동으로 복수의 서버로 확장(복제)되는 기능을 제공한다.


AWS를 이용해 인터넷 어플리케이션을 구축한다는 것은 자체 발전기를 이용해 전력을 생산하기 보다 전력 회사에서 전력을 구매하는 것과 같다. 게다가, 발전기를 자체 운영하는 것과 똑같이 필요에 따라 용량을 조절할 수 있고,  사용량에 따라 비용이 발생하며, 저렴한 비용 등의 장점을 누릴 수 있다. 나아가 대규모 네트워크 운영 경험을 제공 받을 수 있게 된다.


[AWS로 무엇을 할 수 있는가?]

AWS를 이용한다면 물리적 하드웨어를 직접 운영하면서 수행할 수 있는 거의 모든 것을 누릴 수 있다. 웹 사이트, 어플리케이션, 데이터베이스, 모바일 앱, 이메일 발송, 분산시스템 데이터 분석, 컨텐츠 저장소, 그리고 가상 사설망(Virtual Private Network) 등을 구축할 수 있다. AWS 가 제공하는 다양한 서비스들은 면밀히 통합되어 있으며, 수십가지의 서비스가 제공되고 매년 새로운 서비스들이 추가되고 있다.


다음 다어그램은 AWS가 제공하는 기능들을 분류한 것이다.



개별 카테고리에는 하나 이상의 서비스가 포함되어 있다. 예를 들어 AWS는 다섯가지 데이터베이스 서비스를 제공하고 있으며 각각은 용도에 따라 최적화 되어 있다. AWS가 제공하는 다양한 서비스들을 이용해 필요에 맞는 맞춤형 솔루션을 설계할 수 있다.


[가장 먼저 살펴봐야 할 서비스들은 무엇인가?]

AWS는 다양한 서비스들을 제공하기 때문에 무엇부터 시작해야 할지 결정하기 어렵다. 가장 먼저 고려해야 하는 점은 AWS를 이용해 무엇을 할 것인가 이다. AWS의 가장 보편적으로 사용되는 기능 혹은 활용 방식들은 다음과 같다.


  • 파일 저장소
  • 웹 사이트 호스팅
  • 데이터베이스 운영
  • 어플리케이션 실행되는
  • 네트워크 구성
  • AWS 자원 관리


아마존 웹 서비스 홈페이지 자료를 참조해 작성했음을 밝힙니다. (상업적 목적은 없습니다.)

http://docs.aws.amazon.com/gettingstarted/latest/awsgsg-intro/gsg-aws-intro.html



Posted by 곽중선
2014. 4. 22. 23:32

코멘트 (Comment) 리팩토링에 대해 알아보자.

마틴 파울러는 '코드의 부족을 보충하기 위해 상세한 주석이 있는 것' 또한 나쁜 냄새라고 지적했다.


그렇다면 '코드의 부족(lack of code)'이라는 무슨 의미일까? 

작성된 코드가 '대체로 잘 동작하지만, 완벽하다고 말하기는 어렵다는 의미이다.

프로그래머가 일정에 밀려서 미치 완성하지 못한 것일 수도 있고, 정말 귀찮아서 보완하지 못한 것일 수도 있다.

아니면, 실력이 없거나... 누구나 완벽한 것이 가장 좋다고 말하지만, 어디 현실이 그러한가? 

어쩔 수 없다는 핑계와 양해를 주거니 받거니 하면서 살아가는 것이다.


하지만, 백번 양보를 해도 코드가 완벽하지 못하니 알아서 잘 쓰라고 (조심하라는 의미에서...)

주석(comment)에 설명을 붙이는 것은 당신이 가는 길에 지뢰가 있으니 알아서 피하라는 표지판과 다름이 없다.


  • 표지판을 못 볼 수도 있다. (모든 개발자가 주석을 보고 나서 함수를 가져다 쓴다는 보장은 없고,
    현실적으로 많은 이들이 주석을 읽으려 하지 않는다.
  • 표지판을 보고도 그냥 지나칠 수 있다.
    설마하는 사람도 있고, 주석을 보고도 함수(혹은 API)를 가져다 쓰는 사람은 자신에게는 아무런 책임이 없다고 생각하기도 한다.
  • 주석은 실행에 아무런 영향을 주기 때문에, 경고성 주석이 유지보수 혹은 코드 정리 과정에서 사라질 수도 있다.


결론은 프로그램에 잠재적인 오류가 있다거나, 오용(잘못된 사용) 가능성이 있다면
주석으로 문제를 영원히 덮을 수 없다는 것이다.


코딩 사례를 보고 이해해보도록 하자. 많은 실무 프로젝트에서 공통 API (common API)를 만들어 사용하게 되는데 회원 정보를 대다수의 사이트에서 '나이를 계산하는 공통 함수'를 만들어 사용한다. 나이를 계산하기 위해서는 '생년월일'을 입력 받아야 하고, 개발 편의성을 높이기 위해, 굳이 Date 타입으로 입력받지 않고 문자열로 태어난 날짜를 입력 받도록 한다. 여기서 '코드의 부족(lack of code' 문제가 자주 발생하게 된다. 공통 함수이다 보니 가져다 쓰는 곳이 많고, 오류가 발생하더라도 원인 파악이 손쉬워야 함에도 불구하고 간단한 함수라 여겨 대충 만드는 일이 많다.


아래 코드는 주석에 잘못된 입력 값이 들어왔을 경우, 적절한 예외가 발생한다는 것을 명시하였다.

만일, 공통 API를 사용하는 개발자가 주석을 읽지 않고 API를 호출하더라도 잘못된 입력 값(인자 혹은 parameter) 오류의 원인을 파악할 수 있는 '메시지'가 예외 객체에 포함되기 때문에 문제를 빨리 해결할 수 있다. 


만일, 예외처리하는 로직 없이 메소드 주석에 날짜 형식은 'yyyyMMdd'이라는 안내문구를 적어둔 경우는 '코드의 부족을 위해 상세한 주석이 있는 나쁜 냄새가 나는 코드'가 되는 것이다.


package common.util;

import java.sql.Date;

import org.joda.time.DateTime;
import org.joda.time.Years;

/**
 * 업무구분 : 공통 (cn)
 * 업 무 명 : 공통 API
 * 파 일 명 : AgeUtils.java
 * 작 성 일 : 2014-04-22
 * 설    명 : 현재 혹은 기준 일자를 기준으로 한국 나이, 만 나이를 계산하는 유틸리티 메소드들을 제공한다.
 */
public final class AgeUtils {

	private AgeUtils() {
		// This class does not provider public constructor
	}

	/**
	 * 현재 날짜를 기준으로 생년월일에서 한국식 나이를 구한다.
	 * 
	 * AgeUtils.getAge("19731201"); = 41 (현재 년도가 2013년인 경우)
	 * 
	 * @param birthDate 생년월일 (yyyyMMdd 형식)
	 * @return 현재 일자를 기준으로 계산된 한국식 나이
	 * @throws IllegalArgumentException null 혹은 잘못된 형식의 날짜를 입력한 경우 예외 발생
	 */
	public static int getAge(String birthDate) {
		return getAge(birthDate, DateUtils.getTodayString());
	}

	/**
	 * 기준일자를 기준으로 생년월일에서 한국식 나이를 구한다.
	 * 
	 * AgeUtils.getAge("19731201", "20121225"); = 40
	 * 
	 * @param birthDate 생년월일 (yyyyMMdd 형식 혹은 yyyy 형식)
	 * @param refDate 기준일자 (yyyyMMdd 형식 혹은 yyyy 형식)
	 * @return 한국식 나이
	 * @throws IllegalArgumentException null 혹은 잘못된 형식의 날짜를 입력한 경우 예외 발생
	 */
	public static int getAge(String birthDate, String refDate) {
		if (StringUtils.isEmpty(birthDate) || StringUtils.isEmpty(refDate)) {
			throw new IllegalArgumentException("birthDate or refDate parameter is empty.");
		} else if (birthDate.length() < 4 || refDate.length() < 4) {
			throw new IllegalArgumentException("birthDate or refDate parameter length is too short (must longer than 3).");
		} else if (!StringUtils.isNumeric(birthDate) || !StringUtils.isNumeric(refDate)) {
			throw new IllegalArgumentException("birthDate or refDate parameter is not numeric data.");
		}
		if (birthDate.length() == 4) {
			return Integer.parseInt(refDate) - Integer.parseInt(birthDate) + 1;
		} else {
			return getAge(DateUtils.toDate(birthDate), DateUtils.toDate(refDate));
		}
	}

	/**
	 * 생년월일과 기준 일자를 비교하여 한국식 나이를 반환한다.
	 * 
	 * Date birthDate = DateUtils.toDate("20120101");
	 * AgeUtils.getAge(birthDate, DateUtils.toDate("20120201")); = 1
	 * AgeUtils.getAge(birthDate, DateUtils.toDate("20130101")); = 2
	 * 
	 * @param birthDate 생년월일 (yyyyMMdd 형식)
	 * @param refDate 기준일자 (yyyyMMdd 형식)
	 * @return 한국식 나이
	 * @throws IllegalArgumentException null 혹은 잘못된 형식의 날짜를 입력한 경우 예외 발생
	 */
	public static int getAge(Date birthDate, Date refDate) {
		if (birthDate == null || refDate == null) {
			throw new IllegalArgumentException("Invalid argument value. birthDate = '" + birthDate + "', refDate = '" + refDate + "'");
		} else {
			return new DateTime(refDate).getYear() - new DateTime(birthDate).getYear() + 1;
		}
	}

	/**
	 * 현재 일자를 기준으로 생년월일에서 만 나이를 구한다.
	 * 
	 * AgeUtils.getRealAge("19731201"); = 39 (현재 년도가 2013년인 경우)
	 * 
	 * @param birthDate 생년월일 (yyyyMMdd 형식)
	 * @return 만 나이
	 * @throws IllegalArgumentException null 혹은 잘못된 형식의 날짜를 입력한 경우 예외 발생
	 */
	public static int getRealAge(String birthDate) {
		return getRealAge(birthDate, DateUtils.getTodayString());

	}

	/**
	 * 생년월일과 기준 일자를 비교하여 만 나이를 계산한다.
	 * 
	 * AgeUtils.getRealAge("19731201", "20121125"); = 38
	 * AgeUtils.getRealAge("19731201", "20121225"); = 39
	 * 
	 * @param birthDate 생년월일 (yyyyMMdd 형식)
	 * @param refDate 기준일자 (yyyMMdd 형식)
	 * @return 만 나이
	 * @throws IllegalArgumentException null 혹은 잘못된 형식의 날짜를 입력한 경우 예외 발생
	 */
	public static int getRealAge(String birthDate, String refDate) {
		return getRealAge(DateUtils.toDate(birthDate), DateUtils.toDate(refDate));
	}

	/**
	 * 생년월일과 기준 일자를 비교하여 만 나이를 계산한다.
	 * 
	 * Date birthDate = DateUtils.toDate("20120101");
	 * AgeUtils.getRealAge(birthDate, DateUtils.toDate("20120201")); = 0
	 * AgeUtils.getRealAge(birthDate, DateUtils.toDate("20130101")); = 1
	 * 
	 * @param birthDate 생년월일 (Date 타입)
	 * @param refDate 기준일자 (Date 타입)
	 * @return 만 나이
	 * @throws IllegalArgumentException null 혹은 잘못된 형식의 날짜를 입력한 경우 예외 발생
	 */
	public static int getRealAge(Date birthDate, Date refDate) {
		if (birthDate == null || refDate == null) {
			throw new IllegalArgumentException("Invalid argument value. birthDate = '" + birthDate + "', refDate = '" + refDate + "'");
		} else {
			return Years.yearsBetween(new DateTime(birthDate), new DateTime(refDate)).getYears();
		}
	}
}



AgeUtils.java


Posted by 곽중선
2014. 4. 22. 22:50

예전에 Effective Java 학습한 내용을 정리해서 올립니다.

(내용은 차차 추가될 예정입니다.)



Effective Java 1 and 2.ppt


Posted by 곽중선