요약 매도자가 최고 호가로부터 점차 가격을 낮추어 가다가 매수 희망자가 나오면 최초의 매수 희망자에게 매도하는 경매 가격결정 방법.
네덜란드식 경매라고도 한다. 일반적으로 경매는 최저 호가로부터 점차 가격을 높여 가다가 최고 호가의 매수 희망자, 곧 마지막 매수 희망자에게 매도하는 방식을 취한다. 그러나 더치 옥션은 이와는 반대로 매도자가 최고 호가로부터 점차 가격을 낮추어 가다가 매수 희망자가 나오면 최초의 매수 희망자에게 일괄 매도하는 가격결정 방법이다.
이는 채권을 매각할 경우에도 마찬가지로 적용된다. 즉 경매에 참여한 매수 희망자가 자신이 떠안을 수 있는 물량과 가격 조건을 제출하되, 최저 금리를 제시한 물량부터 차례로 채우다가 마지막 물량을 떠안은 매수 희망자가 제시한 금리로 물량을 떠안는 제도를 말한다. 다시 말해 채권을 입찰에 부치되 경매 참여자가 써 낸 금리 가운데 최고치로 발행액 전액을 매각하는 방식이 더치 옥션이다.
1997년 국제통화기금(IMF) 외환위기 당시 외국 금융기관(채권은행)들은 한국의 단기외채 250억 달러를 중장기로 전환할 경우 적용하는 금리를 이 방식으로 결정하자고 제안하였으나, 한국 정부에서는 당사자 협의방식을 주장해 더치 옥션은 수용되지 않았다.
온라인 광고의 노출횟수 대비 클릭 수를 의미한다. 배너광고가 노출된 횟수(Impression) 중, 실제 클릭을 통해 배너광고에 연결된 웹페이지(웹사이트)로 이동한 경우의 비율을 일컫는다. 어떤 온라인 광고의 웹페이지 게재 횟수(노출된 횟수, impression)가 100번이고, 그 광고 클릭 횟수가 1번이면 CTR(CTR=(클릭 수/ 노출된 횟수)×100)은 1%라는 얘기가 된다. 온라인 광고 효과를 측정하는 데 있어 CTR은 중요 지표다. 웹사이트의 방문자를 광고주가 원하는 최종 웹페이지까지 이동하도록 클릭을 유도하는 데 성공한 광고라면, 방문자의 호기심을 자극하는 데 성공했다고 볼 수 있기 때문이다.
현재 온라인상 광고 클릭률(CTR)은 대략 0.2%~0.3%로 매우 낮은 편이나, 아직 그 어떤 예측 분석 솔루션도 어느 광고 캠페인이 어떤 개인으로부터 최고의 클릭률을 유도할 수 있는지 식별하지 못한다. 광고 클릭 과정은 변덕스러우며, 대개 광고를 접할 당시 개인의 기분 과 요구사항이 반영되는 경우가 많다. 전반적으로 모바일 기기가 PC보다 더 높은 배너광고 클릭률을 기록했다. 안드로이드 기기의 평균 클릭률은 PC의 두 배로 나타나 전 세계적으로 안드로이드 기기에서 더 많은 매출이 발생했다.
반면, 미국과 유럽에서는 iOS 기기 매출이 안드로이드를 앞서는 것으로 조사되었다. 모바일 기기는 PC 사용량을 보완하여 추가적인 매출을 창출한다. 일례로 일요일에 발생하는 전자상거래 중 아이패드 내 구매율은 16% 더 높은 반면, 데스크톱 내 구매율은 10% 낮아진다고 발표했다. 또한 모바일 기기에서 가장 높은 평균 구매가는 호텔 및 렌터카 예약과 의류구매에서 발생했으며 각각 약 365달러, 209달러, 105달러(한화 약 36만원, 22만 원, 11만 원)로 조사되었다.
요즘 카이 호스트만의 코어 자바8로 Java 8을 공부하고 있다. 작년 초에 개발자로 전향하여 Java 8의 stream 같은 것을 매우 신기해하며 어깨너머로 배워 사용했었는데 알고보니 Java 8이 처음 출시된 것은 2014년 3월 18일이었다.(Java 릴리스 페이지 참고) 지금은 Java 9가 나오려고 몸을 들썩이는 중이다. Java 8을 제대로 공부도 해보지 않았는데 벌써 Java 9가 나온다고 하니 젊은 나이에 뒤처지는 것 같은 기분이 들었다. 이런 까닭에 황급히 서점에서 책을 사서 공부하는 중이다. 원저의 제목 Core Java for the Impatient가 내 상황에 잘 들어맞는 것 같다. 더 늦기 전에 Java 8을 공부하면서 중요하다고 생각하는 것들을 정리해보려고 한다.
인터페이스
객체 지향 프로그래밍에서 인터페이스는 기능의 생김새만 나타낸다. 인터페이스는 어떤 기능에 대한 추상이며, 실제 구현은 그 인터페이스를 구현하는 클래스에게 맡긴다. 해당 인터페이스를 사용하는 입장에서는 실제 클래스가 어떻게 구현되어 있는지 몰라도 인터페이스의 생김새에 따라 함수를 호출하기만 하면 된다. 마치 복잡한 시스템의 UI(유저 인터페이스)와 같다. 구글 검색 엔진은 복잡한 시스템이지만 사용자에게 보여주는 건 질의어를 입력하는 텍스트 박스밖에 없다. 구글 검색 엔진
추상화가 잘되어 있다는 것은 (구글 검색 엔진처럼)객체의 필요한 기능만 드러내고, 복잡하고 굳이 드러내지 않아도 되는 내용들은 숨겼다는 것을 의미한다. 이전에는 이러한 인터페이스의 추상성을 철저히 지켰기 때문에 인터페이스가 어떤 상태(인스턴스 변수)나 구현된 메서드를 갖는 것이 불가능했다. 하지만 Java 8부터 인터페이스가 조금 더 유연하게 바뀌었다.
정적 메서드
기술적으로 Java에서 인터페이스에 정적 메서드를 추가하지 못할 이유는 없었다. 정적 메서드는 어차피 인스턴스와 관계가 없기 때문이다. 다만 정적 메서드도 구현된 메서드라는 점에서 인터페이스의 추상성을 해친다는 것이 문제였다. Java 8에서는 그러한 제약이 없어졌고, 인터페이스에 정적 메서드를 추가할 수 있게 되었다.(사실 이전에도 인터페이스에 정적 필드는 정의할 수 있었기 때문에 정적 메서드가 Java 8에 와서야 추가된 것은 조금 의아하다.) 기존의 제약을 깨고 정적 메서드를 추가한 것은 개발 편의성을 높이려는 시도로 보인다. Java 8 이전의 표준 라이브러리에서는 인터페이스와 관련된 정적 메서드들을 동반 클래스(companion class)에서 제공했다. 대표적인 예로 Collection 인터페이스와 Collections 동반 클래스가 있다.
Java 8에서는 인터페이스에 기본 구현을 정의할 수 있게되었다. 기본 구현이 제공되는 메서드는 구현 클래스에서 구현하지 않아도 컴파일이 가능하다. 기본 메서드는 기존의 인터페이스에 메서드를 추가해야하는 경우에 아주 유용하다. 인터페이스가 변경되는 일이 없도록 프로그램을 잘 작성하는게 좋겠지만 변경이 불가피한 상황이 생길 수도 있다. 인터페이스에 메서드를 추가하면 해당 인터페이스를 구현하는 모든 클래스에서 추가된 메서드를 구현해야하기 때문에 문제가 생긴다. 구현 클래스가 9개라면 인터페이스까지 10개의 파일을 수정해야 한다. 하지만 추가되는 메서드의 구현이 대부분 동일하다면 인터페이스에 기본적인 메서드 구현을 정의하고 유별난 클래스만 수정해주면 된다. 연료 유형을 포함하는 Car 인터페이스를 예로 들어보자.
publicinterfaceCar{
String fuelType();
}
연료 유형에 따른 구현 클래스들도 있다.
publicclassDieselCarimplementsCar{
@Override
public String fuelType(){
return"DIESEL";
}
}
publicclassGasolineCarimplementsCar{
@Override
public String fuelType(){
return"GASOLINE";
}
}
자동 주행 차량에 발빠르게 대응하기 위해서 Car 인터페이스에 자동 주행 차량 여부를 확인할 수 있는 메서드가 추가되어야한다고 생각해보자. Car는 아래와 같이 변경되어야 한다.
publicinterfaceCar{
String fuelType();
booleanautodrive();
}
이 경우에 autodrive() 메서드는 기본 구현을 제공하지 않으므로 DieselCar, GasolineCar에서 구현해줘야 한다. 하지만 기존 차량들은 자율 주행이 안될 것이기 때문에 아래와 같이 기본 구현을 제공할 수 있다.
publicinterfaceCar{
String fuelType();
defaultbooleanautodrive(){
returnfalse;
}
}
autodrive()는 FutureCar와 같은 유별난 클래스에서만 따로 구현해주면 된다.
publicclassFutureCarimplementsCar{
@Override
public String fuelType(){
return"SOLAR";
}
@Override
publicbooleanautodrive(){
returntrue;
}
}
인터페이스의 기본 메서드는 클래스의 계층을 좀 더 단순하게 만들어준다는 장점도 있다. Java 2부터 있어왔던 AbstractCollection은 Collection 구현 클래스들의 공통 기능을 제공한다. Java 8 이전에는 구현 클래스들의 공통 기능들을 묶기 위해 인터페이스와 구현 클래스 사이에 추상 클래스를 정의하는 것이 일반적이었다. 하지만 Java 8에 와서는 더 이상 추상 클래스를 추가할 필요 없이 기본 메서드를 정의할 수 있게 되었다. 이런 변화로 인터페이스와 추상 클래스의 경계가 모호해졌다는 느낌이 들지만 여전히 인스턴스 변수의 유무 차이는 존재한다.
기본 메서드의 충돌 해결하기
Java에서 하나의 클래스는 여러 인터페이스를 구현할 수 있다. Java 8 이전에는 여러 인터페이스가 같은 메서드를 갖더라도 어차피 구현은 클래스에서만 제공했기 때문에 문제가 되지 않았다. 하지만 Java 8에서 인터페이스들이 각각 동일한 메서드의 기본 구현을 제공하고, 클래스에서 충돌이 발생하는 메서드를 명시적으로 오버라이드 하지 않으면 컴파일러가 어떤 기본 메서드를 사용해야할 지 선택할 수 없기 때문에 문제가 발생한다. 책에 나와있는 예시를 살펴보자.
publicinterfacePerson{
String getName();
defaultintgetId(){ return0; }
}
publicinterfaceIdentified{
defaultintgetId(){ return Math.abs(hashCode()); }
}
publicclassEmployeeimplementsPerson, Identified{
...
}
Person과 Identified 인터페이스는 getId() 기본 메서드를 정의하고 있고, Employee 클래스는 두 인터페이스를 구현한다. Employee 클래스에서 getId()를 오버라이드 하지 않으면 Employee inherits unrelated defaults for getId() from types Person and Identified 라는 컴파일 에러가 발생한다.
한 쪽에서 기본 메서드를 구현하지 않으면 문제가 해결될까? Identified를 아래와 같이 기본 메서드 구현을 하지 않도록 바꾸고 컴파일 해보자.
publicinterfaceIdentified{
intgetId();
}
될 것 같지만 메시지가 Employee is not abstract and does not override abstract method getId() in Identified 라고 바뀔 뿐 여전히 컴파일은 되지 않는다. Person과 Identified 인터페이스가 동일한 메서드를 갖고 있긴 하지만 컴파일러 입장에서는 두 개가 정말 같은 목적의 메서드인지 알 길이 없다. 따라서 같은 모양의 메서드지만 두 메서드를 다른 것으로 보고 클래스가 Identified를 구현하지 않았다고 판단한다.(개인적으로는 이것이 일관성이 부족하다고 생각하는데 그 이유는 두 인터페이스 모두 기본 메서드를 구현하지 않는 경우에는 충돌이 일어나지 않기 때문이다.)
가장 좋은 해결 방안은 충돌이 나는 경우를 만들지 않는 것이다. 두 인터페이스가 동일한 메서드를 갖고 있다면 인터페이스 간에 상속 관계가 있지는 않은지, 메서드 이름을 너무 포괄적으로 정한 것은 아닌지 따져보고 충돌 상황을 피하는 게 좋다.
메서드 이름이나 기본 메서드 구현을 포기하지 않고 컴파일 에러를 해결하는 방법은 아래와 같다.
클래스에서 충돌 메서드 구현
가장 단순한 방법으로 클래스에서 충돌 메서드의 구현을 덮어 버리는 것이다. 이 때 클래스에서 구현을 새로 할 수도 있지만 어느 한 쪽의 기본 메서드를 사용할 수도 있다.
publicclassEmployeeimplementsPerson, Identified{
@Override
publicintgetId(){
// Person의 기본 메서드를 사용하고 싶은 경우.
// Identified의 기본 메서드를 사용하려면 Identified.super.getId()를 반환한다.
return Person.super.getId();
}
}
인터페이스간 상속
Person 인터페이스가 Identified를 상속받도록 하면 Employee에 구현 메서드가 없어도 문제를 해결할 수 있다. 하지만 이런 결정을 하기 전에 인터페이스 사이의 관계를 잘 고려해야한다.