http://charlie0301.blogspot.kr/2017/12/java-api.html
http://charlie0301.blogspot.kr/2017/12/java-api-5-6.html
책이 API 디자인 외에 한번쯤 library, framework을 고민한다면 읽어 볼 만함.
일부만 추려서 적었지만 책에는 더 좋은 내용이 자~알 설명 되어 있음.
------------------------------------------------------------------------------------
Chapter 7 : 모듈화 아키텍처를 사용하라
1. 모듈화 설계의 유형
라이브러리
모듈화 라이브러리들을 사용 시 구현이 중복일 수 있고 이 때 최신 API JAR만 로드 되어 링크 될 수 있도록 해야 함.
모듈화 라이브러리의 경우 각 모듈의 구현체를 접근할 수 있는 생성자, 정적 팩터리 메서드가 주로 사용되고 다른 방법으로는 프로퍼티 기반 방법도 가능하다.
모듈화 라이브러리의 "명세"는 인터페이스나 한두 개의 팩터리가 포함된 일부 패키지를 노출하는 모듈이며 해당 명세에서 허용되는 행위를 설명하는 문서도 동반한다.
"구현"은 단순히 명세 모듈에 의존하고 팩터리 구현체를 서비스 등록 시스템이 등록하는 노출된 패키지가 없는 모듈에 해당 됨.
"클라이언트"는 명세 모듈에 의존하고 팩터리 메서드를 호출하는 모듈에 해당되는
2. 상호컴포넌트 룩업과 통신
모듈화 설계의 유일한 목적은 애플리케이션의 개별 부분들을 다른 부분과 분리하는 데 있음.
두개의 모듈을 서로 독립적으로 만들고 싶다면 서로에 관해 알아서는 안되고 인터페이스를 이용해 상호작용해야 한다.
모듈 사용 시 환경 설정을 해야할 필요가 있는데 이 때 대부분의 프레임워크에서 하는 일은 의존성 주입(dependency injection)을 하며 주로 설정파일을 사용할 때가 많다.
- 의존성 주입
*** launcher 예제
모듈화 라이브러리에서는 적절한 초기화를 위해 registerXYZ를 수행하는 팩터리 클래스가 필요하다.
단 코딩 스타일의 미적 측면을 약화시킬 수 있고 절차적 등록으로 인해 (모듈갯수, 로드, 링크, 실행시간) 구동속도를 느리게 할 수 있다.
- 호출 이 필요없는(call-less) 연동 (프로퍼티기반)
*** system.getproperty 예제
연동코드를 실행할 필요가 없고 등록을 좀 더 선언적인 방식으로 수행
주로 System.getProperty()를 호출하는 방법임.
JDK에서 흔희 사용하는 괜찮은 방법이고 호출이 필요없는 등록을 허용하고 지연연동(late binding)도 가능하다.
app 개발자가 registerXYZ 호출 코드는 필요 없지만 일종의 설정을 제공해야 한다.
이는 앱 개발자에게 코드 작성 & 컴파일 & 실행 보다는 낫지만 그래도 클래스의 이름이나 시스템 동작에 대한 지식은 필요하다.
프로퍼티 기반 설정 중 널리 알려진 형식 중 하나는 스프링 프레임워크에서 제공하는 것으로 XML 설정 파일이다.
XML 설정 파일 예제
- 어노테이션 지정
스프링 2.5에서는 구현 클래스에 어노테이션을 지정하는 옵션을 제공함.
*** 어노테이션 예제
어노테이션으로 노출한 빈과 해당 빈의 이름, 다른 API에 대한 잠재적인 의존성까지 지정함.
app 개발자는 더 이상 모든 구현체 클래스의 이름을 참조하는 설정 파일을 작성할 필요가 없음.
*** 빈 설정 예제
이전 방식보다 알아야 할 지식의 양은 적어 졌지만 그럼에도 모든 구현체가 담긴 패키지의 이름은 알아야 함.
- Look Up
*** look up 예제
Lookup.getDefault()의 기본 구현은 JDK의 확장 메커니즘을 기반으로 하기 때문에 아무런 설정파일이 필요 없고 클래스패스만 올바르게 구성하면 된다.
org.apidesign.anagram.api.WordLibrary 와 같은 서비스를 찾을 경우
Lookup.getDefault().lookupAll(org.apidesign.anagram.api.WordLibrary.class) 를 실행하게 되고 이후
구현체에서는 현재 클래스패스를 발견하고 META-INF/services/org.apidesign.anagram.api.WordLibrary에 위치한 모든 리소스를 읽어 클래스명을 알아내기 위해 분석하고
클래스를 인스턴스화 한 후 해당 인스턴스들을 반환한다.
JDK6부터는 등록된 서비스 구현체의 전체목록을 조회하는 공개 API가 존재한다.
*** 예제
3. 확장점 작성하기
왓???
4. 순환 의존성의 필요성
순환 의존성은 레거시 코드를 다룰 때 꽤나 유용하다. 반면 이러한 기능을 새로 작성한 코드에 사용하는 것은 클래스와 모듈 간의 참조로 구성된 하나의 커다란 스파게티 접시를 만드는 것과 같다.
상호 참조는 Lookup을 사용하여 의존성을 제거할 수 있다.
또한 각각 독립적으로 실행되지 않으므로 필요로하는 의존성은 manifest에 명시를 하여 적절히 처리되도록 하는게 필요하다.
5. Lookup은 어디에나 있다.
JDK의 ServiceLoader와 달리 넷빈즈의 Lookup은 다수의 인스턴스를 가질 수 있고 각각은 개별 풀의 역할을 수행한다.
예를 들어 아이콘이 java.awt.Image로 변환되어야 할 필요성이 있는 경우 아이콘이 해당 class를 이미 가지고 있다면 loader에서 찾아 제공하고 아니면 이미지를 만들어 처리하면 된다.
*** 아이콘 예제
Lookup을 이벤트 버스 (event bus)로 생각할 수 있다. 객체가 들어오고 나오고 하는 곳 같이..
*** Modifiable 예제
다른 유용한 것으로는 질의 패턴(query pattern)이다. Lookup의 두가지 측면을 혼합한 것인데 적응형 패턴(adaptable pattern)에 대한 API가 될 수 있다는 것과 Lookup이 컴포넌트 주입에 대한 파사드가 될 수 있다는 것을 의미한다.
6. Lookup의 남용
Lookup의 동적인 특성은 대단히 흥미롭고 감동적이어서 불필요하게 사용할 때가 있다.
Lookup이 흥미로운 객체로 가득 찬 "마법 가방"의 역할을 하게 되는 것이고 API 사용자는 가방안에 손을 집어 넣어 아무것도 안 보이는 채로 특정 인터페이스를 찾는다.
인터페이스가 있다면 제대로 동작할 것이고 없다면 완전히 망가질 것이다.
------------------------------------------------------------------------------------
Chapter 8 : 클라이언트와 제공자를 위한 API를 분리하라.
클라이언트를 위한 API와 플러그인을 위한 서비스 제공자 인터페이스(SPI)는 다르다.
API 진화는 SPI 진화와 다르다.
클라이언트에게 메서드를 제공하는 API의 경우 추가와 관련된 문제는 없다.
새로운 메서드를 추가한다고 해서 바이너리 호환성이 깨지는 것은 아니고 클라이언트 입장에서는 더 많은 가능성과 선택권을 갖게 된다.
. SPI의 경우 정확히 그 반대다.
다른사람들이 반드시 제공해야만 하는 인터페이스에 새로운 메서드를 추가하면 사실상 기존의 모든 구현체가 망가진다.
. API를 적절히 나눠라.
API와 SPI를 분리하는 이유는 진화 문제 때문이었다.
다른 접근 법은 가독성이다.
모든 API(특히 자바로 작성된 API)는 지역성(locality)을 보이며 관련성 있는 것들은 서로 가까운 곳에 정의된다.
사용자에 관해 생각해 보고 사용자가 API에 기대할 만한 지역성을 사용자에게 제공하라.
- 관련 메서드는 같은 클래스에 배치한다.
- 관련이 없는 메서드를 어디에 둬야 할지 생각해낼 수 없다는 이유로 그것들이 이어써야 할 자리가 아닌 곳에 놓아서는 안 된다.
- 관련 클래스는 한 패키지 안에 넣어라.
- 특별한 상황에 유용한 추가적인 클래스는 다른 곳으로 옮긴다.
위 규칙들을 따르면 API의 지역성이 증가하고 결과적으로 API 내에서의 사용자 지향성이 대폭 증가할 것이다.
여러분이 만든 라이브러리의 인터페이스를 훨씬 더 많은 부분으로 나누는 것이 타당하다.
다음은 API를 네가지 서로 다른 범주로 정의한 넷빈즈 모듈의 예다.
- 핵심 API : 이 핵심 API를 사용하지 않고는 불가능한 일부 핵심적인 연산을 수행하고자 하는 평범한 관심사를 가진 사용자 그룹
- 지원 API : API 사용을 용이하게 하는 유틸리티 메서드 집합. 이러한 유틸리티 메서드는 반드시 사용해야 하는 것은 아니지만 API 사용자에게 편의성을 제공한다.
- 핵심 SPI : 라이브러리에 연결(plug in)하고자 하는 다양한 그룹의 사용자를 위한 인터페이스 집합.
- 지원 SPI : 이따금 연결할 인터페이스를 구현하는 것이 다소 복잡할 때가 있다. 다시금 도우미 인터페이스를 제공할 수 있다.
위는 넷빈즈의 특정 모듈에 어떤 것이 효과적인지만 보여주는 것이고
다만 넷빈즈 프로젝트를 진행하면서 저지른 실수로부터 얻은 교훈 중 하나는 바로 API가 사용의 지역성을 따른 다면 API 사용자의 삶이 향상된다는 것이다.
2017년 12월 18일 월요일
2017년 12월 11일 월요일
JAVA API 디자인, 5-6장
http://charlie0301.blogspot.kr/2017/12/java-api.html
책이 API 디자인 외에 한번쯤 library, framework을 고민한다면 읽어 볼 만함.
일부만 추려서 적었지만 책에는 더 좋은 내용이 자~알 설명 되어 있음.
------------------------------------------------------------------------------------
5장 : 필요 이상으로 노출하지 마라.
무언가를 API로 만들것이냐 만들지 말 것이냐를 결정하는데 유스케이스를 만들어 보라
특정 메서드나 클래스에 대한 유스 케이스가 없거나 미심쩍다면 API에 해당 요소를 빼는 편이 낫다.
API를 처음 릴리스하기 앞서 제거할 수 있는 것은 모조리 제거하라는 것이다.
1. 필드를 감춰라, 직접 노출하기 보다는 필드에 접근하는 메서드(getter, setter)를 사용하는 편이 낫다.
읽기, 쓰기만 가능한 필드 보다 추가작업을 할 수 있다.
2. 생성자보다 팩터리가 낫다.
생성자 대신 팩터리 메서드를 노출할 경우 나중에 API를 진화시키기 수월해진다.
1. 팩터리의 반환 클래스 타입을 유연(다형성)하게 제공할 수 있다.
2. 인스턴스를 캐싱할 수 있다.
3. 동기화가 가능하다.
3. 모든 것을 final로 만들어라
서브 클래싱을 허용하는데는 생각치 못한 결과가 발생할 수 있으므로 class를 final로 만들어라.
4. 어울리지 않는 곳에 설정자 메서드를 넣지 마라.
설정자(setXXXX)는 구현 세부사항을 API 사용자가 아는 상황에서 사용 가능하다.
API의 기능을 풍부하게 만들기 위한 방법으로 주로 사용되는데 역효과(유지보수, API 시맨틱 파악 어려움)가 발생한다.
5. 프랜드 코드에서만 접근하는 것을 허용하라.
API에서 너무 많은 것들을 호출하지 않으려고 할 때 또 한가지 유용한 기법은 특정 기능에 대한 접근 권한을 "프랜드(friend)" 코드에만 부여하는 것이다.
- "Only for internal use", "Do not call me" 이딴 설명을 API에 적지 말고 접근권한 변경으로 사용자에게서 숨겨라.
6. 객체를 만든 이에게 더 많은 권한을 부여하라.
(TBD)
7. 깊은 계층구조를 노출하지 마라.
(TBD)
------------------------------------------------------------------------------------
6장 : 구현이 아닌 인터페이스를 대상으로 코드를 작성하라.
1. 메서드나 필드 제거하기
일단 공개된 인터페이스나 클래스에서 메서드나 필드를 제거하는 것은
기존 API 사용 코드들에서 컴파일 에러를 발생 시키고 나아가서 바이너리 호환성도 보장하지 않는다.
private, package private, protected abstract(설계 실수)는 제거 가능
public, protected(설계 실수) 는 제거 불가
2. 클래스나 인터페이스를 제거하거나 추가하기
제거는 소스및 바이너리 차원에서 문제를 일으킴, 공개된 것들은 계속 유지하는 것을 권고
추가는 문제 없음. 다만 와일드카드 임포트를 이용할 경우 소스 호환성이 깨질 수 있음.
3. 기존 계층 구조에 인터페이스나 클래스 집어 넣기
가능, 제약 조건은 원본 및 새로운 타입에서 접근할 수 있었던 메서드 전체가 동일하게 유지되거나 최소한 줄어들지 않아야함.
4. 메서드나 필드 추가하기
필드의 경우 앞서 말한 것과 같이 바이너리 호환성을 지원할 수 있는 static final이 아닌 이상 API에 추가해서는 안된다.
정적 메서드를 추가하는 것은 바이너리 관점에서 수용할만하지만 클래스에서만 사용해야 한다.
정적 메서드와 중복 정의된 비슷한 메서드의 변종들로 비호환성을 야기하거나 컴파일 오류를 만들 수 있다.
서브클래싱이 가능한 클래스에 추상 메서드를 추가하는 것은 호환성을 깨뜨리는 변경사항에 해당됨. 이는 인터페이스를 사용하는 구현에서 런타임 예외를 발생 시킬 수 있다.
즉 추상메서드를 서브클래싱이 가능한 클래스와 인터페이스에 추가하는 것을 최소화 해야 하던가 클래스와 인터페이스를 서브클래싱 할 수 없게끔 만들어야 한다.
5. 자바 인터페이스와 클래스 비교
자바 인터페이스의 가장 큰 기능은 다중 상속이지만 실제로 성능, 점유 메모리의 양을 최소화 하기 위함이다.
다중 상속이면 하나의 객체로 API 인터페이스를 무제한 상속 가능하지만 클래스의 경우 API 클래스 마다 하나의 하위 클래스를 생성해야 하고 다른 클래스와 연관된 경우 위임을 통해서 인스턴시를 연결해야 한다.
단 한 두개 보다 상당한 객체들을 생성하는 경우에만 상당한 효과가 있다.
6. 외유내강
인터페이스 관점에서 기존 인터페이스에 메서드를 추가하는 것이 다소 어렵다.
하지만 이를 인터페이스의 강점으로 대처할 수 있는데
각기 버전마다 인터페이스를 만들어 제공하는 것이 방법이 될 수 있다.
Language13, Language14, Lauguage15 와 같이 인터페이스르 만들어 제공하면 사용자는 필요한 것을 선택해 사용함.
단 너무 인터페이스가 급격히 늘어나면 클라이언트 코드가 복잡해 질 수 있다.
7. 메서드를 추가하길 좋아하는 사람들의 천국
메서드가 완전히 바이너리 호환성을 보장하도록 하게 위해서는 final 클래스 형태로 표현한다.
이는 서브클래싱도 막을 수 있고 인터페이스나 추상 클래스에 메서드가 추가됐을 때도 문제는 없지만
추가된 메서드를 호출되는 경우에는 문제가 된다
클래스 파일에서 메서드의 이름과 매개변수, 반환형을 기준으로 호출된 메서드를 완전히 구분하므로
이름과 인자수가 같은 메서드를 추가하지 않아야 문제를 방지할 수 있다.
8. 추상 클래스는 유용한다.
지금까지 불변적인 계약을 정의하고 싶을 때 자바 인터페이스를 사용하고
메서드를 추가할 능력을 갖추고 싶을 때 자바 final 클래스를 사용해야 한다고 주장
반면에 추상 클래스를 사용할 이유는 실제로 없다.
자바 인터페이스와 비교 시 자바 추상 클래스의 한가지 이점은 정적 메서드를 포함할 수 있어 추상 클래스에 정적 팩터리 메서드를 만들 필요성이 있다면 사용 가능하다.
자바 인터페이스에 비해 자바 클래스의 유용한 특징은 접근 권한을 제한할 수 있다.
추상클래스를 만들어 제공할 경우 구현할 수 있는 클래스 제한은 런타임 시 클래스 타입을 보고 제한할 수 있다.
9. 매개변수 증가를 위한 대비
response/reply 패턴?
10. 인터페이스 대 클래스
구현이 아닌 인터페이스(자바 인터페이스가 아니라 추상적인 정의)를 대상으로 코드를 작성하라.
자바 인터페이스를 사용해서 불변적인 타입을 명시하고 메서드를 안전하게 추가할 수 있는 곳에서는 final 클래스를 타입으로 사용한다.
목표가 완전한 바이너리 호환성을 보장하는 것이라면 다른 누군가가 구현할 수 있는 클래스나 인터페이스에 절대 메서드를 추가해서는 안된다.
책이 API 디자인 외에 한번쯤 library, framework을 고민한다면 읽어 볼 만함.
일부만 추려서 적었지만 책에는 더 좋은 내용이 자~알 설명 되어 있음.
------------------------------------------------------------------------------------
5장 : 필요 이상으로 노출하지 마라.
무언가를 API로 만들것이냐 만들지 말 것이냐를 결정하는데 유스케이스를 만들어 보라
특정 메서드나 클래스에 대한 유스 케이스가 없거나 미심쩍다면 API에 해당 요소를 빼는 편이 낫다.
API를 처음 릴리스하기 앞서 제거할 수 있는 것은 모조리 제거하라는 것이다.
1. 필드를 감춰라, 직접 노출하기 보다는 필드에 접근하는 메서드(getter, setter)를 사용하는 편이 낫다.
읽기, 쓰기만 가능한 필드 보다 추가작업을 할 수 있다.
2. 생성자보다 팩터리가 낫다.
생성자 대신 팩터리 메서드를 노출할 경우 나중에 API를 진화시키기 수월해진다.
1. 팩터리의 반환 클래스 타입을 유연(다형성)하게 제공할 수 있다.
2. 인스턴스를 캐싱할 수 있다.
3. 동기화가 가능하다.
3. 모든 것을 final로 만들어라
서브 클래싱을 허용하는데는 생각치 못한 결과가 발생할 수 있으므로 class를 final로 만들어라.
4. 어울리지 않는 곳에 설정자 메서드를 넣지 마라.
설정자(setXXXX)는 구현 세부사항을 API 사용자가 아는 상황에서 사용 가능하다.
API의 기능을 풍부하게 만들기 위한 방법으로 주로 사용되는데 역효과(유지보수, API 시맨틱 파악 어려움)가 발생한다.
5. 프랜드 코드에서만 접근하는 것을 허용하라.
API에서 너무 많은 것들을 호출하지 않으려고 할 때 또 한가지 유용한 기법은 특정 기능에 대한 접근 권한을 "프랜드(friend)" 코드에만 부여하는 것이다.
- "Only for internal use", "Do not call me" 이딴 설명을 API에 적지 말고 접근권한 변경으로 사용자에게서 숨겨라.
6. 객체를 만든 이에게 더 많은 권한을 부여하라.
(TBD)
7. 깊은 계층구조를 노출하지 마라.
(TBD)
------------------------------------------------------------------------------------
6장 : 구현이 아닌 인터페이스를 대상으로 코드를 작성하라.
1. 메서드나 필드 제거하기
일단 공개된 인터페이스나 클래스에서 메서드나 필드를 제거하는 것은
기존 API 사용 코드들에서 컴파일 에러를 발생 시키고 나아가서 바이너리 호환성도 보장하지 않는다.
private, package private, protected abstract(설계 실수)는 제거 가능
public, protected(설계 실수) 는 제거 불가
2. 클래스나 인터페이스를 제거하거나 추가하기
제거는 소스및 바이너리 차원에서 문제를 일으킴, 공개된 것들은 계속 유지하는 것을 권고
추가는 문제 없음. 다만 와일드카드 임포트를 이용할 경우 소스 호환성이 깨질 수 있음.
3. 기존 계층 구조에 인터페이스나 클래스 집어 넣기
가능, 제약 조건은 원본 및 새로운 타입에서 접근할 수 있었던 메서드 전체가 동일하게 유지되거나 최소한 줄어들지 않아야함.
4. 메서드나 필드 추가하기
필드의 경우 앞서 말한 것과 같이 바이너리 호환성을 지원할 수 있는 static final이 아닌 이상 API에 추가해서는 안된다.
정적 메서드를 추가하는 것은 바이너리 관점에서 수용할만하지만 클래스에서만 사용해야 한다.
정적 메서드와 중복 정의된 비슷한 메서드의 변종들로 비호환성을 야기하거나 컴파일 오류를 만들 수 있다.
서브클래싱이 가능한 클래스에 추상 메서드를 추가하는 것은 호환성을 깨뜨리는 변경사항에 해당됨. 이는 인터페이스를 사용하는 구현에서 런타임 예외를 발생 시킬 수 있다.
즉 추상메서드를 서브클래싱이 가능한 클래스와 인터페이스에 추가하는 것을 최소화 해야 하던가 클래스와 인터페이스를 서브클래싱 할 수 없게끔 만들어야 한다.
5. 자바 인터페이스와 클래스 비교
자바 인터페이스의 가장 큰 기능은 다중 상속이지만 실제로 성능, 점유 메모리의 양을 최소화 하기 위함이다.
다중 상속이면 하나의 객체로 API 인터페이스를 무제한 상속 가능하지만 클래스의 경우 API 클래스 마다 하나의 하위 클래스를 생성해야 하고 다른 클래스와 연관된 경우 위임을 통해서 인스턴시를 연결해야 한다.
단 한 두개 보다 상당한 객체들을 생성하는 경우에만 상당한 효과가 있다.
6. 외유내강
인터페이스 관점에서 기존 인터페이스에 메서드를 추가하는 것이 다소 어렵다.
하지만 이를 인터페이스의 강점으로 대처할 수 있는데
각기 버전마다 인터페이스를 만들어 제공하는 것이 방법이 될 수 있다.
Language13, Language14, Lauguage15 와 같이 인터페이스르 만들어 제공하면 사용자는 필요한 것을 선택해 사용함.
단 너무 인터페이스가 급격히 늘어나면 클라이언트 코드가 복잡해 질 수 있다.
7. 메서드를 추가하길 좋아하는 사람들의 천국
메서드가 완전히 바이너리 호환성을 보장하도록 하게 위해서는 final 클래스 형태로 표현한다.
이는 서브클래싱도 막을 수 있고 인터페이스나 추상 클래스에 메서드가 추가됐을 때도 문제는 없지만
추가된 메서드를 호출되는 경우에는 문제가 된다
클래스 파일에서 메서드의 이름과 매개변수, 반환형을 기준으로 호출된 메서드를 완전히 구분하므로
이름과 인자수가 같은 메서드를 추가하지 않아야 문제를 방지할 수 있다.
8. 추상 클래스는 유용한다.
지금까지 불변적인 계약을 정의하고 싶을 때 자바 인터페이스를 사용하고
메서드를 추가할 능력을 갖추고 싶을 때 자바 final 클래스를 사용해야 한다고 주장
반면에 추상 클래스를 사용할 이유는 실제로 없다.
자바 인터페이스와 비교 시 자바 추상 클래스의 한가지 이점은 정적 메서드를 포함할 수 있어 추상 클래스에 정적 팩터리 메서드를 만들 필요성이 있다면 사용 가능하다.
자바 인터페이스에 비해 자바 클래스의 유용한 특징은 접근 권한을 제한할 수 있다.
추상클래스를 만들어 제공할 경우 구현할 수 있는 클래스 제한은 런타임 시 클래스 타입을 보고 제한할 수 있다.
9. 매개변수 증가를 위한 대비
response/reply 패턴?
10. 인터페이스 대 클래스
구현이 아닌 인터페이스(자바 인터페이스가 아니라 추상적인 정의)를 대상으로 코드를 작성하라.
자바 인터페이스를 사용해서 불변적인 타입을 명시하고 메서드를 안전하게 추가할 수 있는 곳에서는 final 클래스를 타입으로 사용한다.
목표가 완전한 바이너리 호환성을 보장하는 것이라면 다른 누군가가 구현할 수 있는 클래스나 인터페이스에 절대 메서드를 추가해서는 안된다.
2017년 12월 4일 월요일
Java API 디자인 책, ~4장
필요에 의해 이 책을 보게 되었는데..
책에 써 있는대로 3일만에 뚝딱 읽을 수 있는 책이 아님.
너무 말이 많다. 하지만 내용은 좋아서 두고 보고 싶지만 대출기간이 짧아서 좀..
http://www.yes24.com/24/goods/17713587?scode=032
독자분들께,
일단 읽은 부분 까지만 간략히 정리를 하려고 했는데
도저히 1-3장은 요약을 못하겠음. 대신 목차만 보자면..
[1부] 이론과 정당성
나머지는 요약 불가...
1장: 현대 소프트웨어 구축의 예술
합리주의, 경험주의, 무지
지금까지의 소프트웨어의 진화
거대한 기반 요소
아름다움, 진리, 우아함
더 무지해져라!
2장: API를 만드는 이유
분산 개발
애플리케이션 모듈화
비선형적인 버전 관리
중요한 것은 의사소통이다
경험적 프로그래밍
첫 번째 버전은 늘 쉽다
3장: 훌륭한 API를 결정하는 요소
메서드와 필드 시그너처
파일과 파일의 내용
환경변수와 명령줄 옵션
API로서의 텍스트 메시지
프로토콜
동작 방식
I18N 지원과 L10N 메시지
넓은 의미의 API
API의 품질을 검사하는 법
4장: 시시각각 변하는 표적
1. 첫 번째 버전은 결코 완벽하지 않다.
API 개선 시 API 사용자에게 문제를 일으키지 않도록 설계해야 한다.
2. 하위 호환성
. 소스 호환성
= API가 변경되어도 컴파일 될 수 있는가?
클래스에 새 메서드를 추가하는 것도 소스 호환성을 깰 수 있다.
필요 없는 class들을 import하는 wildcard import 로 호환성이 깨질 수 있다.
. 바이너리 호환성
= 이전 버전의 라이브러리를 가지고 컴파일된 프로그램이 다시 컴파일하지 않고도 새로운 버전의 API와 링크 될 수 있는가?
이로 인해
1. 애플리케이션을 작성한 다음 그것을 한 버전의 라이브러리와 컴파일하는 동시에 사람들이 다른 버전으로도 사용하게 만들 수 있고
애플리케이션의 유지보수와 패키징, 배포가 대폭 간소화 된다.
2. 이전 버전의 라이브러리를 대상으로 빌드 된 바이너리만 가지고 있는 사용자가 애플리케이션이 재 컴파일될 때까지 기다리지 않고도
새로운 버전의 라이브러리로 마이그레이션 할 수 있다.
가상머신이 메서드를 찾을 때는 모든 가능한 상위 클래스까지 찾는데 비해 필드를 찾을 때는 지정된 클래스에서만 필드를 찾는다.
결과적으로 필드대신 메서드를 노출하는 편이 추후를 위해서 낫다.
객체 지향 언어에서 메서드를 선언한는 것과 재정의하는 것과 관련해서 가상 메서드 테이블(virtual methods table)를 알아야 한다.
final 키워드를 지정하지 않은 메서드가 여러개 포함된 클래스를 정의할 경우 사실상 테이블을 하나 만들게 된다.
이 테이블에는 메서드의 이름들(프로토타입 포함)이 해당 메세드를 호출 했을 때 실제로 실행되는 코드와 매핑되어 있다.
하위 클래스를 만들면 앞에서 말한 테이블이 만들어지고 이 테이블은 특정 메서드가 호출됐을 때 실행되는 새로운 코드를 가리키는 다른 포인터들로 채워진다.
이후 메서드를 호출하는 코드에서는 항상 테이블을 검사해서 시그너처를 기준으로 올바를 메서드를 찾아 테이블에 등록된 실제코드를 호출한다.
API 클래스에 public static final 문자열이나 정수 상수를 정의한다면 평범한 object나 다른 타입의 경우처럼 참조되지 않고 대신 그것을 참조하는 클래스로 복사된다. 이는 새 버전의 API에서 원시 상수 같은 것들의 값을 변경하더라도 해당 API의 클라이언트(이미 사용중인)가 보는 값은 변경되지 않는 다는 것을 의미한다.
. 기능적 호환성
= 어떤 App을 새 버전의 라이브러리를 대상으로 실행했을 때와 이전 버전의 라이브러리를 대상으로 실행했을 때 동일한 결과를 보이는가?
이를 위해서는 API가 어떤 일을 해야 할지 명확하게 이해하고 그것을 실제 프로그래밍 언어로 표현하는 기술이 탁월해야 하며, API 사용자가 API를 어떻게 사용할 지 예상할 수 있어야 한다.
. 유스 케이스 지향의 중요성
API를 통해 무슨일을 할 수 있고 왜 해야 하고 어떻게 할 수 있는지를 파악하기 위해 API 사용자에 대해 생각해볼 필요가 있다.
유스 케이스는 인공적인 것에 불과하고 현실과 거리가 멀 수도 있고 API 실제 사용자의 요구사항과 다를 수 있다.
유스 케이스는 API에서 의도하는 사용법을 설명한 것으로 잠재 사용자가 겪을 수 있는 문제를 구체적인 해결책 없이 보여준다.
잠재적인 문제를 사람들이 겪을 가능성이 높은 실제 문제와 최대한 가깝게 만드는 것은 진짜 유용한 해결책(API)을 만들어 내기 위한 훌륭한 첫걸음이다.
시나리오는 추상적인 유스 케이스를 API에서 실제로 실현된 내용과 연결된다. 시나리오에서는 어떤 클래스나 인터페이스를 구현해야 하고.
그것들을 등록하려면 선언적인 파일을 어디에 어떻게 넣어야 할지 기술한다.
. API 검토
API 변경 요청을 제출 시 다음의 "성공적인 API 설계를 위한 규칙"을 충족하는 지 검사한다.
- 유스 케이스 기반 API 설계
: API 설계 동기가 추상적이고 일반적인 수준으로 기술돼 있으며, 이를 통해 실제 시나리오에 대한 일반적인 설계 결정과 API가 최종적으로 실현된(javadoc) 결과물이 연결된다.
- 일관성 있는 API 설계
: 개별 개발자가 만든 API는 반드시 팀 전체에서 공유하는 일반적인 "모범 사례"와 부합해야 한다.
- 단순하고 깔끔한 API 설계
: 간단하고 공통적인 작업은 쉬워야 한다. 중요 유스케이스가 구현하기 쉬운 시나리오를 통해 충족되는지 확인할 수 있다.
- 더 적은 것이 더 많다.
: 유스 케이스에 기술된 대로 딱 필요한 기능만 노출돼야 한다. 이렇게 하면 API 사용 범위의 예상과 현실간 차이를 방지할 수 있다.
- 진화 대비
: 라이브러리는 반드시 나중에 유지 보수할 수 있어야 한다. 새로운 요구사항이 나타나거나 기존에 유지 보수하던 사람이 떠나더라도 API가 위태로워져서는 안 된다.
. API 생명주기
넷빈즈의 API 범주
- 비공개(Private) : 접근 가능하나 모듈과 라이브러리 외부에서 사용할 용도로 만들어진 것은 아닌 기능에 부여하는 범주
- 프렌드(Friend) : API는 시스템의 다른 부분(한정된 영역)에서 사용하기 위해 고안 된 범주
- 개발 중(Under development) : 안정적인 API 될 것으로 예상되는 불완전한 계약
- 안정(Stable) : 제품 품질에 도달했고 공개적으로 사용할 준비가 된 인터페이스에 사용됨.
- 공식(Official) : API는 안정적이면서 제품에 패키징 된 상태
- 서드파티(Third-party) : 인터페이스는 내부 규칙을 따르지 않는 측에서 제공된 범주, 이러한 인터페이스를 노출해서는 안된다.
- 표준(Standard) : 서드파티 분류와 비슷하다.
- 지원중단(Deprecated) : 어느정도 시간이 흐른 후 거의 모든 API는 현재 상태와 상관없이 쓸모가 없어지거나 새로운 API로 대체된 경우 해당되는 범주
. 점진적 향상
점진적 변경 vs 빅뱅 방식
점진적 변경 : 라이브러리 기능은 보전하면서 새로운 메서드나 클래스를 추가하거나 새로운 엘리먼트를 추가하는 것
빅뱅 방식 : 보통 기존 API를 완전히 개조해서 새로운 버전을 만들어내는 행위
"사소"한 점진적 변경이라도 API 사용에 심각한 악영향을 줄 수 있다.
빅뱅은 하위 호환성에 대해 현실적이지만 사용자들이 새로운 API를 사용하고 적응하는데 시간과 비용이 필요하고 사용자들은 뭔가 새로운 장점이 있을 것이라고 기대할 수 있다.
결론적으로는 빅뱅방식에 비해 피해가 적은 점진적 변경을 준비해야 한다. 하지만 API가 형편 없어 점진적 변경이 불가하다면 빅뱅방식의 변경이 정당화 될 수 는 있다. 단 API 사용자들의 고통을 수반한다는 것을 염두해야 한다.
나머지는 나중에..
책에 써 있는대로 3일만에 뚝딱 읽을 수 있는 책이 아님.
너무 말이 많다. 하지만 내용은 좋아서 두고 보고 싶지만 대출기간이 짧아서 좀..
http://www.yes24.com/24/goods/17713587?scode=032
독자분들께,
서점에서 이 책을 손에 들고 “이 책을 사야 할까?”라고 자문하고 계신다면 저는 이렇게 답하겠습니다. 만약 여러분이 작성한 코드가 다른 사람들이 작성한 코드를 컴파일하는 데 필요하다면 여러분은 API의 세계에 들어갈 준비가 된 것입니다. 그리고 이 책은 여러분이 API의 세계를 탐험하도록 도와줄 것입니다.
하지만 이 책은 “5번의 손쉬운 강의로 API 설계를 가르쳐 드리지” 않습니다. 이 책은 “단 3일만에” 읽을 수 있는 책이 아닙니다! 빠르게 읽을 수 있는 지침서를 찾고 계신다면 아마 이 책은 아닐 것입니다. 한편으로 API 설계에 관한 깊이 있는 지식, 즉 API 설계에 대한 “어떻게”뿐만 아니라 “왜”에도 관심이 있다면 이 책을 책장에 다시 내려놓으시기 전에 저를 잠깐 소개해 드리겠습니다.
일단 읽은 부분 까지만 간략히 정리를 하려고 했는데
도저히 1-3장은 요약을 못하겠음. 대신 목차만 보자면..
[1부] 이론과 정당성
애플리케이션 프로그래밍 인터페이스(API; Application Programming Interface)를 만들고 설계하고 작성하는 과정은
예술적인 탐구이자 과학적인 탐구로 볼 수 있음.
관점에 따라 API 아키텍트는 세상을 바꾸려는 예술가이거나 여러 세계를 잇는 다리를 만들려는 기술자에 해당한다.
나머지는 요약 불가...
1장: 현대 소프트웨어 구축의 예술
합리주의, 경험주의, 무지
지금까지의 소프트웨어의 진화
거대한 기반 요소
아름다움, 진리, 우아함
더 무지해져라!
2장: API를 만드는 이유
분산 개발
애플리케이션 모듈화
비선형적인 버전 관리
중요한 것은 의사소통이다
경험적 프로그래밍
첫 번째 버전은 늘 쉽다
3장: 훌륭한 API를 결정하는 요소
메서드와 필드 시그너처
파일과 파일의 내용
환경변수와 명령줄 옵션
API로서의 텍스트 메시지
프로토콜
동작 방식
I18N 지원과 L10N 메시지
넓은 의미의 API
API의 품질을 검사하는 법
4장: 시시각각 변하는 표적
1. 첫 번째 버전은 결코 완벽하지 않다.
API 개선 시 API 사용자에게 문제를 일으키지 않도록 설계해야 한다.
2. 하위 호환성
. 소스 호환성
= API가 변경되어도 컴파일 될 수 있는가?
클래스에 새 메서드를 추가하는 것도 소스 호환성을 깰 수 있다.
필요 없는 class들을 import하는 wildcard import 로 호환성이 깨질 수 있다.
. 바이너리 호환성
= 이전 버전의 라이브러리를 가지고 컴파일된 프로그램이 다시 컴파일하지 않고도 새로운 버전의 API와 링크 될 수 있는가?
이로 인해
1. 애플리케이션을 작성한 다음 그것을 한 버전의 라이브러리와 컴파일하는 동시에 사람들이 다른 버전으로도 사용하게 만들 수 있고
애플리케이션의 유지보수와 패키징, 배포가 대폭 간소화 된다.
2. 이전 버전의 라이브러리를 대상으로 빌드 된 바이너리만 가지고 있는 사용자가 애플리케이션이 재 컴파일될 때까지 기다리지 않고도
새로운 버전의 라이브러리로 마이그레이션 할 수 있다.
가상머신이 메서드를 찾을 때는 모든 가능한 상위 클래스까지 찾는데 비해 필드를 찾을 때는 지정된 클래스에서만 필드를 찾는다.
결과적으로 필드대신 메서드를 노출하는 편이 추후를 위해서 낫다.
객체 지향 언어에서 메서드를 선언한는 것과 재정의하는 것과 관련해서 가상 메서드 테이블(virtual methods table)를 알아야 한다.
final 키워드를 지정하지 않은 메서드가 여러개 포함된 클래스를 정의할 경우 사실상 테이블을 하나 만들게 된다.
이 테이블에는 메서드의 이름들(프로토타입 포함)이 해당 메세드를 호출 했을 때 실제로 실행되는 코드와 매핑되어 있다.
하위 클래스를 만들면 앞에서 말한 테이블이 만들어지고 이 테이블은 특정 메서드가 호출됐을 때 실행되는 새로운 코드를 가리키는 다른 포인터들로 채워진다.
이후 메서드를 호출하는 코드에서는 항상 테이블을 검사해서 시그너처를 기준으로 올바를 메서드를 찾아 테이블에 등록된 실제코드를 호출한다.
API 클래스에 public static final 문자열이나 정수 상수를 정의한다면 평범한 object나 다른 타입의 경우처럼 참조되지 않고 대신 그것을 참조하는 클래스로 복사된다. 이는 새 버전의 API에서 원시 상수 같은 것들의 값을 변경하더라도 해당 API의 클라이언트(이미 사용중인)가 보는 값은 변경되지 않는 다는 것을 의미한다.
. 기능적 호환성
= 어떤 App을 새 버전의 라이브러리를 대상으로 실행했을 때와 이전 버전의 라이브러리를 대상으로 실행했을 때 동일한 결과를 보이는가?
이를 위해서는 API가 어떤 일을 해야 할지 명확하게 이해하고 그것을 실제 프로그래밍 언어로 표현하는 기술이 탁월해야 하며, API 사용자가 API를 어떻게 사용할 지 예상할 수 있어야 한다.
. 유스 케이스 지향의 중요성
API를 통해 무슨일을 할 수 있고 왜 해야 하고 어떻게 할 수 있는지를 파악하기 위해 API 사용자에 대해 생각해볼 필요가 있다.
유스 케이스는 인공적인 것에 불과하고 현실과 거리가 멀 수도 있고 API 실제 사용자의 요구사항과 다를 수 있다.
유스 케이스는 API에서 의도하는 사용법을 설명한 것으로 잠재 사용자가 겪을 수 있는 문제를 구체적인 해결책 없이 보여준다.
잠재적인 문제를 사람들이 겪을 가능성이 높은 실제 문제와 최대한 가깝게 만드는 것은 진짜 유용한 해결책(API)을 만들어 내기 위한 훌륭한 첫걸음이다.
시나리오는 추상적인 유스 케이스를 API에서 실제로 실현된 내용과 연결된다. 시나리오에서는 어떤 클래스나 인터페이스를 구현해야 하고.
그것들을 등록하려면 선언적인 파일을 어디에 어떻게 넣어야 할지 기술한다.
. API 검토
API 변경 요청을 제출 시 다음의 "성공적인 API 설계를 위한 규칙"을 충족하는 지 검사한다.
- 유스 케이스 기반 API 설계
: API 설계 동기가 추상적이고 일반적인 수준으로 기술돼 있으며, 이를 통해 실제 시나리오에 대한 일반적인 설계 결정과 API가 최종적으로 실현된(javadoc) 결과물이 연결된다.
- 일관성 있는 API 설계
: 개별 개발자가 만든 API는 반드시 팀 전체에서 공유하는 일반적인 "모범 사례"와 부합해야 한다.
- 단순하고 깔끔한 API 설계
: 간단하고 공통적인 작업은 쉬워야 한다. 중요 유스케이스가 구현하기 쉬운 시나리오를 통해 충족되는지 확인할 수 있다.
- 더 적은 것이 더 많다.
: 유스 케이스에 기술된 대로 딱 필요한 기능만 노출돼야 한다. 이렇게 하면 API 사용 범위의 예상과 현실간 차이를 방지할 수 있다.
- 진화 대비
: 라이브러리는 반드시 나중에 유지 보수할 수 있어야 한다. 새로운 요구사항이 나타나거나 기존에 유지 보수하던 사람이 떠나더라도 API가 위태로워져서는 안 된다.
. API 생명주기
넷빈즈의 API 범주
- 비공개(Private) : 접근 가능하나 모듈과 라이브러리 외부에서 사용할 용도로 만들어진 것은 아닌 기능에 부여하는 범주
- 프렌드(Friend) : API는 시스템의 다른 부분(한정된 영역)에서 사용하기 위해 고안 된 범주
- 개발 중(Under development) : 안정적인 API 될 것으로 예상되는 불완전한 계약
- 안정(Stable) : 제품 품질에 도달했고 공개적으로 사용할 준비가 된 인터페이스에 사용됨.
- 공식(Official) : API는 안정적이면서 제품에 패키징 된 상태
- 서드파티(Third-party) : 인터페이스는 내부 규칙을 따르지 않는 측에서 제공된 범주, 이러한 인터페이스를 노출해서는 안된다.
- 표준(Standard) : 서드파티 분류와 비슷하다.
- 지원중단(Deprecated) : 어느정도 시간이 흐른 후 거의 모든 API는 현재 상태와 상관없이 쓸모가 없어지거나 새로운 API로 대체된 경우 해당되는 범주
. 점진적 향상
점진적 변경 vs 빅뱅 방식
점진적 변경 : 라이브러리 기능은 보전하면서 새로운 메서드나 클래스를 추가하거나 새로운 엘리먼트를 추가하는 것
빅뱅 방식 : 보통 기존 API를 완전히 개조해서 새로운 버전을 만들어내는 행위
"사소"한 점진적 변경이라도 API 사용에 심각한 악영향을 줄 수 있다.
빅뱅은 하위 호환성에 대해 현실적이지만 사용자들이 새로운 API를 사용하고 적응하는데 시간과 비용이 필요하고 사용자들은 뭔가 새로운 장점이 있을 것이라고 기대할 수 있다.
결론적으로는 빅뱅방식에 비해 피해가 적은 점진적 변경을 준비해야 한다. 하지만 API가 형편 없어 점진적 변경이 불가하다면 빅뱅방식의 변경이 정당화 될 수 는 있다. 단 API 사용자들의 고통을 수반한다는 것을 염두해야 한다.
나머지는 나중에..
2017년 11월 10일 금요일
iOS, Samsung IAP, Unity Overview
iOS IAP
https://developer.apple.com/in-app-purchase/
IAP 대상
: Content, Subscriptions, New features, Services.
IAP 아이템 종류
Consumable
: app에서 사용되는 아이템들이 해당되고 구입해서 한번 소비하면 삭제되어 재구매 가능.
Non-Consumable
: app에서 소비되지 않거나 premium feature 같은 것이 해당되고 구입하면 만료없이 사용 가능.
Auto-Renewable Subscriptions
: 서비스나 업데이트된 콘텐츠를 주기적으로 구입하는 상황이 해당 됨. (cloud storage 매달 사용, 주간 magazine 배달 받음)
사용자가 주기적인 구입을 취소하지 않는 한 지정된 기한마다 자동 결재 됨.
Non-Renewing Subscriptions
: 사용자가 서비스나 콘텐츠를 지정된 기간동안만 사용 가능하고 자동으로 subscription이 갱신되지 않는다.
[Preparing]
- Paid Application Agreement 작성 & banking, tax 정보 입력
- Xcode를 사용하여 app에서 IAP service를 사용하도록 함.
- iTunes Connect에서 상품명, 가격, 설명 등을 작성하여 관리한다.
[Designing and Building]
- IAP store design
- IAP 구현
https://developer.apple.com/library/content/documentation/NetworkingInternet/Conceptual/StoreKitGuide/Introduction.html#//apple_ref/doc/uid/TP40008267
[Testing]
- Apple sandbox에서 IAP를 영향 없이 테스트 할 수 있음.
- TestFlight를 사용하여 테스터들을 대상으로 IAP을 테스트하고 feedback을 받을 수 있음.
*실제 구현 관련 포스팅
http://korea-developer.tistory.com/entry/iOS-IAPInApp-Purchase
-----------------------------------------------------------------------------------------------
Samsung IAP
Mobile
http://developer.samsung.com/iap
http://developer.samsung.com/html/techdoc/HowToStart_SamsungInAppPurchaseSDK_v3.0.0_KR.pdf
- Samsung In-App Purchase SDK 3.0.1 이상에서는 Android 6.0(Mashmallow) 이상에서 사용 가능
- 그외 버전에서는 Android 4.0 이상에서 사용 가능한것으로 보임
- Payment 방식 : 90개국에서 Credit Card, Phone bill, PayPal 가능
- 삼성 (Galaxy) Device만 지원
- Samsung Gear에서도 Tizen SDK + Extension SDK를 사용해서 IAP 사용가능
- IAP v2.0 : Consumable item, Non-Consumable items, Non-Recurring subscription items.
IAP v3.0 : Auto-Recurring subscriptions item.
IAP v5.0 : Item, Subscription으로 정리, Non-Recurring subscription 제거됨.
[API]
Android API : http://developer.samsung.com/iap/guide
Server Side API? : http://developer.samsung.com/iap/developer-api
Gear
http://developer.samsung.com/gear/distribute/iap- Mobile(Galaxy)와 연동되어 동작하는 것으로 보임.
- Gear에서 사용할 수 있는 API는 상품 목록, 구매, 구입 목록 확인만 있음.
[Gear API]
. Web : http://img-developer.samsung.com/onlinedocs/gear/web_device/webapis/iap.html
. Native(C) : http://img-developer.samsung.com/onlinedocs/gear/native/group__CAPI__IAP__GALAXYAPPS__FRAMEWORK.html
[Programming Guide]
. Web : http://img-developer.samsung.com/onlinedocs/gear/Extension_PG_Web/html/prog_guide/inapp_ww.htm
. Native(C) : http://img-developer.samsung.com/onlinedocs/gear/Extension_PG_Native/html/prog_guide/inapp_wn.htm
TV
http://developer.samsung.com/tv/tizen-net-tv-preview/guides/samsung-checkout
- Samsung Checkout 을 통해 Samsung TV 상에서 구매 가능.
- 3-step checkout ???
: 사용자가 구매 수단 등록 (mobile, web)
: 구매 아이템 정보 확인
: Remote control로 숫자를 입력하여 구매 진행
- 환경
. TV: Samsung Smart TV, 2015 or later model (excluding Evolution Kit)
. Payment method: Credit or debit card, PayPal, or carrier billing (in Korea)
. Security: Tizen SecureIME, 2nd-screen card registration (mobile or PC)
. Account: Samsung Account (for sharing account and payment information with Galaxy phones)
- 사전 절차
: TV Sellor Office에 app 등록, DPI portal에 Billing 정보 등록 및 코딩
(DPI(Digital Product Inventory) security key와 service를 사용하여 Purchase 가능
- 주요 기능
: 아이템 목록 확인, 아이템 구매, 구매 목록 확인, 구매 확인, 구매 복구, 구독 취소
[Billing 사용을 위한 Precondition]
- NuGet packages
: Tizen.TV
: Tizen.TV.Service.Billing
: Tizen.TV.Service.Sso
- Privileges
<tizen:privilege name="http://developer.samsung.com/privilege/sso.partner"/>
<tizen:privilege name="http://developer.samsung.com/privilege/productinfo"/>
<tizen:privilege name="http://tizen.org/privilege/appmanager.launch"/>
<tizen:privilege name="http://developer.samsung.com/privilege/billing"/>
- 기타 환경 설정
a. Retrieve the User ID:
using Tizen.TV.Service.Sso;
string loginUid = Sso.GetLoginUid(); //get UID value from TV
b. Retrieve the country code:
using Tizen.TV;
string country = Environment.SmartHubConfig.Country;
c. Retrieve the server type:
using Tizen.TV;
int serverType = Environment.SmartHubConfig.ServerType;
d. Set the service environment:
// Use dummy payment for development
BillingRequestServerType servertype = BillingRequestServerType.Dummy;
// When submitting to Samsung Apps TV, use Sandbox or Live environment depending on the TV environment.
if(serverType == 0) { // Operating zone
BillingRequestServerType servertype = BillingRequestServerType.Prd;
} else { // Staging zone
BillingRequestServerType servertype = BillingRequestServerType.Dev;
}
string securityKey = "**********"; // YOUR SECURITY KEY ISSUED BY DPI PORTA
Sample : https://github.com/SamsungDForum/Xamarin.Billing
-----------------------------------------------------------------------------------------------
Unity IAP
https://docs.unity3d.com/Manual/UnityIAPSettingUp.html
- 지원 Store (뭐 그냥 다 된다고 보면 된다.)
: Apple App Store, Mac App Store, Google Play Store, Windows Store, Amazon Appstore, Galaxy Store, Tizen Store, CloudMoolah MOO Store
- Product Types
: Consumable, Non Consumable, Subscription (별도로 구분하고 있지는 않는 듯)
- 주요 기능
: IAP 아이템 목록 추가/확인, 아이템 구입,
구매 목록 복구, 구매 확인, 구매 검증, Store 확장 기능
- Store Extensions
: https://docs.unity3d.com/Manual/UnityIAPStoreExtensions.html
: Cross Platform purchase flow에서 처리할 수 없는
Store의 unique한 process를 처리하도록 만든 Interface.
/// <summary> /// Called when Unity IAP is ready to make purchases. /// </summary> public void OnInitialized (IStoreController controller, IExtensionProvider extensions) { extensions.GetExtension<IAppleExtensions> ().RefreshAppReceipt (result => { if (result) { // Refresh finished successfully. } else { // Refresh failed. } }); }
2017년 10월 31일 화요일
[Links] OKR
이전에 좀 보긴했지만
잘정리된 사이트라 보관 차원에서 링크 저장
번역하신분께서 적어두셨듯 (http://infuture.kr/m/1629)
영어로 인해 제대로 보지 못했었는데
아래 링크에서 잘 정리해 주신듯 함.
연재니 계속 읽어보고 정리해야겠음.
Android IAP 대충 정리
안드로이드 IAP가 궁금해서 대충 요약함.
https://developer.android.com/google/play/billing/billing_subscriptions.html
[인앱구독]
인앱 결재를 사용하여 구독 서비스 판매 가능.
- 다양한 주기의 구독 결재 판매 가능
- 월간, 연간 구독을 위해 체험 기간 구성 가능
- Google Play Developer API나 Developer Console을 사용하여 구독 관리
- 사용자는 앱내에서 구독 구매
- 구독 갱신, 업그레이드, 다운그레이드 가능
- 구독 결재 연기 가능
구독은 개발자가 지정한 간격으로 자동 반복되는 결재
인앱 상품과 유사한 절차로 구독을 구성하고 게시함. 단 체험기간과 결재 주기 선택이 다름
[구독 아이템 구성]
[구독 가격]
- 사용 가능 통화로 구독 가격 설정.
- 구독 가격은 0원 초과
- 구독 가격을 다르게 설정 가능(ex 월간 요금 vs 할인된 연간 요금)
[사용자 결제]
- 주간, 월간, 3개월 6개월, 연간, 시즌
[수동 갱신]
- 구독 중 추가 구독 시 만료일이 추가 구독 기간을 포함하여 연장 됨.
[구독 업그레이드/다운그레이드]
- 구독을 변경할 경우 기존 구독이 취소되고 새 구독이 생성됨.
[결제 유예]
- 유예기간 동안 컨텐트 구독 및 사용 권한을 가지지만 요금이 처구되지 않음.
- 단 유예할 수 있는 최장기간은 API 호출 당 1년 이고 1년 중 다시 호출하여 유예기간 연장 가능
[무료 평가판]
- 무료 체험 기간을 설정할 수 있음.
- 개발자가 지정한 기간 동안만 체험이 가능하고 기간이 지나면 구독 결재 주기와 가격에 따라 관리되는 구독으로 자동 전환됨.
- 일반적인 구매 절차를 따라 구매하지만 무료 체험기간 동안은 요금이 0.00 임.
[구독 취소]
- App이 아닌 Play Store의 앱의 My Apps 화면에서 구독에 대한 상태 확인, 취소 가능.
- 구독 취소 시 Google Play는 결재 주기에 대해서는 환불하지 않고 결제 주기 종료까지 구독에 액세스 할 수 있음.
[앱 제거]
- 사용자가 구매한 구독을 포함한 앱 제거시 구독 정보를 알려줌.
- App을 제거하여도 구독은 제거되지 않음.
[결제 처리 및 정책]
- Google Payments를 통해서만 판매 가능
- 구독 상품 구매 시 Transaction 요금은 구매와 같이 30%임.
- 매 구독 마다 구매 정보(판매자 주문 번호와 각각 반복 transaction 정보를 제공함)
[Google Play Developer API]
https://developer.android.com/google/play/billing/billing_integrate.html#Subs
[인앱 프로모션]
https://developer.android.com/google/play/billing/billing_promotions.html?hl=ko
- 모든 프로모션 코드는 특정한 상품ID(SKU)와 연결됨.
- 프로모션 코드를 입력하면 특정 아이템의 가격을 완불한 것과 같이 처리됨.
[프로모션 코드 생성 및 사용]
- 프로모션 코드는 Google Play Developer console에서 생성
- 각 프로모션 코드는 Developer console의 단일 상품과 연결
- 프로모션 코드는 일반 인앱 결제와 동일한 절차를 거치되 단 돈대신 프로모션 코드를 사용한다는 점이 다름.
- Google Play 스토어 앱에서 코드를 사용할 수 있음.
[앱에서 프로모션 코드 지원]
- 앱이 다시 시작(onResume())될 때마다 getPurchases() 메서드 호출 후 구매내역 확인
- 앱이 IAP를 지원한다면 구매와 유사하게 진행 되므로 별도 처리 필요 없음.
- 앱 실행 중에 Play 스토어 앱에서 프로모션 코드를 사용하는 경우 처리가 필요함.
: com.android.vending.billing.PURCHASES_UPDATED intent 수신을 위한 처리 필요.
(등록)
(취소)
[보안 및 디자인]
https://developer.android.com/google/play/billing/billing_best_practices.html?hl=ko
- 잠금 해제된 콘텐츠를 apk 파일에 포함하여 재배포 할 수 있게 하지 말라.
- 코드 난독 처리 필요, proguard 실행, 아래 방법도 사용가능
. 메서드를 다른 메서드로 인라인 처리합니다.
. 문자열을 상수로 정의하는 대신 즉석에서 생성합니다.
. Java 리플렉션(reflection)을 사용하여 메서드를 호출합니다.
- 보안 랜덤 Nonce를 사용하라.
- 잠금 해제된 콘텐츠를 사용할 때 마다 취소 여부를 확인하라.
- Google Play 공개키를 안전하게 보관하라.
. 문자열로 삽입하지 말고 런타임에 문자열 생성이나 비트조작을 사용하여 숨겨라.
https://developer.android.com/google/play/billing/billing_subscriptions.html
[인앱구독]
인앱 결재를 사용하여 구독 서비스 판매 가능.
- 다양한 주기의 구독 결재 판매 가능
- 월간, 연간 구독을 위해 체험 기간 구성 가능
- Google Play Developer API나 Developer Console을 사용하여 구독 관리
- 사용자는 앱내에서 구독 구매
- 구독 갱신, 업그레이드, 다운그레이드 가능
- 구독 결재 연기 가능
구독은 개발자가 지정한 간격으로 자동 반복되는 결재
인앱 상품과 유사한 절차로 구독을 구성하고 게시함. 단 체험기간과 결재 주기 선택이 다름
[구독 아이템 구성]
- 구매 유형: 항상 Subscription으로 설정
- 구독 ID: 구독 식별자
- 게시 상태: 게시 안 됨/게시됨
- 언어: 구독을 표시하기 위한 기본 언어
- 제목: 구독 상품의 제목
- 설명: 사용자에게 구독에 대해 설명하는 세부정보
- 가격: 반복 구매 시마다 적용되는 기본 구독 가격
- 반복 구매: 반복 구매 결제 주기
- 추가적인 통화 가격(자동 채우기 가능)
[구독 가격]
- 사용 가능 통화로 구독 가격 설정.
- 구독 가격은 0원 초과
- 구독 가격을 다르게 설정 가능(ex 월간 요금 vs 할인된 연간 요금)
[사용자 결제]
- 주간, 월간, 3개월 6개월, 연간, 시즌
[수동 갱신]
- 구독 중 추가 구독 시 만료일이 추가 구독 기간을 포함하여 연장 됨.
[구독 업그레이드/다운그레이드]
- 구독을 변경할 경우 기존 구독이 취소되고 새 구독이 생성됨.
[결제 유예]
- 유예기간 동안 컨텐트 구독 및 사용 권한을 가지지만 요금이 처구되지 않음.
- 단 유예할 수 있는 최장기간은 API 호출 당 1년 이고 1년 중 다시 호출하여 유예기간 연장 가능
[무료 평가판]
- 무료 체험 기간을 설정할 수 있음.
- 개발자가 지정한 기간 동안만 체험이 가능하고 기간이 지나면 구독 결재 주기와 가격에 따라 관리되는 구독으로 자동 전환됨.
- 일반적인 구매 절차를 따라 구매하지만 무료 체험기간 동안은 요금이 0.00 임.
[구독 취소]
- App이 아닌 Play Store의 앱의 My Apps 화면에서 구독에 대한 상태 확인, 취소 가능.
- 구독 취소 시 Google Play는 결재 주기에 대해서는 환불하지 않고 결제 주기 종료까지 구독에 액세스 할 수 있음.
[앱 제거]
- 사용자가 구매한 구독을 포함한 앱 제거시 구독 정보를 알려줌.
- App을 제거하여도 구독은 제거되지 않음.
[결제 처리 및 정책]
- Google Payments를 통해서만 판매 가능
- 구독 상품 구매 시 Transaction 요금은 구매와 같이 30%임.
- 매 구독 마다 구매 정보(판매자 주문 번호와 각각 반복 transaction 정보를 제공함)
[Google Play Developer API]
- 언제든지 특정 구독의 유효성을 원격으로 쿼리
- 구독 취소
- 구독의 다음 결제일 유예
- 구독을 취소하지 않고 구독 결제 대금 환불
- 구독 환불 및 취소
https://developer.android.com/google/play/billing/billing_integrate.html#Subs
구독 구현
구독에 대한 구매 흐름을 시작하는 것은 상품에 대한 구매 흐름을 시작하는 것과 비슷하지만, 상품 유형을 "subs"로 설정해야 한다는 점이 다릅니다. 인앱 상품의 경우에서와 정확히 똑같이, 액티비티의
onActivityResult
메서드로 구매 결과가 전달됩니다.Bundle bundle = mService.getBuyIntent(3, "com.example.myapp", MY_SKU, "subs", developerPayload); PendingIntent pendingIntent = bundle.getParcelable(RESPONSE_BUY_INTENT); if (bundle.getInt(RESPONSE_CODE) == BILLING_RESPONSE_RESULT_OK) { // Start purchase flow (this brings up the Google Play UI). // Result will be delivered through onActivityResult(). startIntentSenderForResult(pendingIntent, RC_BUY, new Intent(), Integer.valueOf(0), Integer.valueOf(0), Integer.valueOf(0)); }
활성 구독을 쿼리하려면 이때도 상품 유형 매개변수를 "subs"로 설정하여
getPurchases
메서드를 사용하세요.Bundle activeSubs = mService.getPurchases(3, "com.example.myapp", "subs", continueToken);
이렇게 호출하면 사용자가 소유한 모든 활성 구독을 포함한
Bundle
이 반환됩니다. 구독이 만료되고 갱신하지 않으면 반환되는 Bundle
에 더 이상 구독이 표시되지 않습니다.[인앱 프로모션]
https://developer.android.com/google/play/billing/billing_promotions.html?hl=ko
- 모든 프로모션 코드는 특정한 상품ID(SKU)와 연결됨.
- 프로모션 코드를 입력하면 특정 아이템의 가격을 완불한 것과 같이 처리됨.
[프로모션 코드 생성 및 사용]
- 프로모션 코드는 Google Play Developer console에서 생성
- 각 프로모션 코드는 Developer console의 단일 상품과 연결
- 프로모션 코드는 일반 인앱 결제와 동일한 절차를 거치되 단 돈대신 프로모션 코드를 사용한다는 점이 다름.
- Google Play 스토어 앱에서 코드를 사용할 수 있음.
[앱에서 프로모션 코드 지원]
- 앱이 다시 시작(onResume())될 때마다 getPurchases() 메서드 호출 후 구매내역 확인
- 앱이 IAP를 지원한다면 구매와 유사하게 진행 되므로 별도 처리 필요 없음.
- 앱 실행 중에 Play 스토어 앱에서 프로모션 코드를 사용하는 경우 처리가 필요함.
: com.android.vending.billing.PURCHASES_UPDATED intent 수신을 위한 처리 필요.
(등록)
IntentFilter promoFilter = new IntentFilter("com.android.vending.billing.PURCHASES_UPDATED"); registerReceiver(myPromoReceiver, promoFilter);
(취소)
unRegisterReceiver(myPromoReceiver);
[보안 및 디자인]
https://developer.android.com/google/play/billing/billing_best_practices.html?hl=ko
- 잠금 해제된 콘텐츠를 apk 파일에 포함하여 재배포 할 수 있게 하지 말라.
- 코드 난독 처리 필요, proguard 실행, 아래 방법도 사용가능
. 메서드를 다른 메서드로 인라인 처리합니다.
. 문자열을 상수로 정의하는 대신 즉석에서 생성합니다.
. Java 리플렉션(reflection)을 사용하여 메서드를 호출합니다.
- 보안 랜덤 Nonce를 사용하라.
- 잠금 해제된 콘텐츠를 사용할 때 마다 취소 여부를 확인하라.
- Google Play 공개키를 안전하게 보관하라.
. 문자열로 삽입하지 말고 런타임에 문자열 생성이나 비트조작을 사용하여 숨겨라.
2017년 10월 30일 월요일
Android IAP references, 개요, 아이템 구매
Android IAP가 궁금해서 대충 요약 해 봄.
: https://developer.android.com/google/play/billing/index.html?hl=ko
대상 : 표준 인앱 상품(일회성 결제), 구독(반복 자동 결제)
- IAP를 사용하여 아이템 판매 시 Google Play가 모든 결제 정보를 처리하여 App은 결재 transaction 관련 처리가 필요 없음.
- Google Play에서 제공하는 결재 서비스를 사용하므로 모든 app들에 대해 일관적인 경험 제공
- Google Play를 통해 세기되는 App 대상
(개요)
[In-app Billing API]
[Google Play Developer Console]
: https://developer.android.com/google/play/billing/billing_overview.html?hl=ko#api
- Google Play 앱에 의해 노출 되는 API를 통해 결제 서비스 사용
- Google Play 앱이 App<->Google Play server 사이 결제 요청, 응답 전달
- App<->Google Play 앱은 IPC를 사용하여 결제 요청, 응답을 받음.
- 결재는 Google Play 서버를 통해서 진행하므로 Google Play app이 서버와 통신할 수 있어야 함.
- Android In-app billing version은 현재 v3가 최신 버전임.
. App은 google Play의 API를 통해 상품 세부 정보 요청, 상품 주문, 소유 상품 복원 가능.
. API는 구매 완료 시 주문 정보를 동기적으로 기기로 전파.
. 모든 구매가 Google Play를 통해 관리됨.
. 구매한 상품은 소비 가능하며 소비 시 소유하지 않은 상태로 변경됨.
. 구독 서비스를 지원함.
- IAP 사용 App을 게시하고 IAP로 판매할 상품을 관리(구매/구독, 상품 목록 생성)함.
- 제품에 대한 다음 정보들을 정의함.
. 상품 ID(SKU), 상품 유형, 가격, 설명, 구매에 대한 처리 및 추적?
https://developer.android.com/google/play/billing/billing_reference.html?hl=ko#getSkuDetails
[Google Play 구매 흐름]
1. App >- IAP 상품 결재 요청 -> Google Play
2. Google Play 결재 양식 요청, 유효성 검사, 구매 transaction 처리
3. Google Play >- 구매 세부 정보(주문 번호, 날짜, 시간, 가격 등) -> Google Play
(In-app Billing API)
: https://developer.android.com/google/play/billing/api.html?hl=ko#producttypes
: https://developer.android.com/google/play/billing/billing_integrate.html?hl=ko
* 구현방법이 좀 다름(확인 필요). https://developer.android.com/training/in-app-billing/preparing-iab-app.html?hl=ko
[아이템 구매]
* 구독에 대한 구매 흐름은 유사하지만 상품 유형이 subs로 설정해야 함.
(요청)
* Transaction 무결성을 보장하기 위해 Google Play는 구매 응답 데이터를 포함한 JSON 문자열에 서명함.
[인앱 상품 소비]
구매한 상품은 소유된 상태로 간주되고 소유된 상태는 Google Play에서 다시 구매할 수 없음. 하지만 소유한 상품을 소비 하면 구매 데이터가 삭제 되고 소유되지 않는 상태로 변경됨.
[구독]
[홍보]
[보안 및 디자인]
[테스트]
[관리]
: https://developer.android.com/google/play/billing/index.html?hl=ko
대상 : 표준 인앱 상품(일회성 결제), 구독(반복 자동 결제)
- IAP를 사용하여 아이템 판매 시 Google Play가 모든 결제 정보를 처리하여 App은 결재 transaction 관련 처리가 필요 없음.
- Google Play에서 제공하는 결재 서비스를 사용하므로 모든 app들에 대해 일관적인 경험 제공
- Google Play를 통해 세기되는 App 대상
(개요)
[In-app Billing API]
[Google Play Developer Console]
: https://developer.android.com/google/play/billing/billing_overview.html?hl=ko#api
- Google Play 앱에 의해 노출 되는 API를 통해 결제 서비스 사용
- Google Play 앱이 App<->Google Play server 사이 결제 요청, 응답 전달
- App<->Google Play 앱은 IPC를 사용하여 결제 요청, 응답을 받음.
- 결재는 Google Play 서버를 통해서 진행하므로 Google Play app이 서버와 통신할 수 있어야 함.
- Android In-app billing version은 현재 v3가 최신 버전임.
. App은 google Play의 API를 통해 상품 세부 정보 요청, 상품 주문, 소유 상품 복원 가능.
. API는 구매 완료 시 주문 정보를 동기적으로 기기로 전파.
. 모든 구매가 Google Play를 통해 관리됨.
. 구매한 상품은 소비 가능하며 소비 시 소유하지 않은 상태로 변경됨.
. 구독 서비스를 지원함.
- IAP 사용 App을 게시하고 IAP로 판매할 상품을 관리(구매/구독, 상품 목록 생성)함.
- 제품에 대한 다음 정보들을 정의함.
. 상품 ID(SKU), 상품 유형, 가격, 설명, 구매에 대한 처리 및 추적?
https://developer.android.com/google/play/billing/billing_reference.html?hl=ko#getSkuDetails
키 | 설명 |
---|---|
productId | 상품의 ID입니다. |
type | 이 키의 값은 인앱 상품의 경우 “inapp” , 구독의 경우 "subs" 여야 합니다. |
price | 아이템의 형식 지정된 가격(통화 기호 포함)으로, 세금을 제외한 가격입니다. |
price_amount_micros | 마이크로 단위의 가격으로, 1,000,000 마이크로 단위가 1 통화 단위와 같습니다. 예를 들어, price 가 "€7.99" 이면 price_amount_micros 는 "7990000" 입니다. 이 값은 특정 통화에 대해 현지화된 반올림 가격을 나타냅니다. |
price_currency_code | price 에 대한 ISO 4217 통화 코드입니다. 예를 들어, price 가 영국 파운드 단위로 지정되어 있는 경우 price_currency_code 는 "GBP" 입니다. |
title | 상품의 제목입니다. |
description | 상품에 대한 설명입니다. |
[Google Play 구매 흐름]
1. App >- IAP 상품 결재 요청 -> Google Play
2. Google Play 결재 양식 요청, 유효성 검사, 구매 transaction 처리
3. Google Play >- 구매 세부 정보(주문 번호, 날짜, 시간, 가격 등) -> Google Play
(In-app Billing API)
: https://developer.android.com/google/play/billing/api.html?hl=ko#producttypes
: https://developer.android.com/google/play/billing/billing_integrate.html?hl=ko
* 구현방법이 좀 다름(확인 필요). https://developer.android.com/training/in-app-billing/preparing-iab-app.html?hl=ko
[아이템 구매]
구매 물품 확인
Bundle ownedItems = mService.getPurchases(3, getPackageName(), "inapp", null);
int response = ownedItems.getInt("RESPONSE_CODE"); if (response == 0) { ArrayList<String> ownedSkus = ownedItems.getStringArrayList("INAPP_PURCHASE_ITEM_LIST"); ArrayList<String> purchaseDataList = ownedItems.getStringArrayList("INAPP_PURCHASE_DATA_LIST"); ArrayList<String> signatureList = ownedItems.getStringArrayList("INAPP_DATA_SIGNATURE_LIST"); String continuationToken = ownedItems.getString("INAPP_CONTINUATION_TOKEN"); for (int i = 0; i < purchaseDataList.size(); ++i) { String purchaseData = purchaseDataList.get(i); String signature = signatureList.get(i); String sku = ownedSkus.get(i); // do something with this purchase information // e.g. display the updated list of products owned by user } // if continuationToken != null, call getPurchases again // and pass in the token to retrieve more items }
인앱 상품 세부정보 확인
Bundle skuDetails = mService.getSkuDetails(3, getPackageName(), "inapp", querySkus);
int response = skuDetails.getInt("RESPONSE_CODE"); if (response == 0) { ArrayList<String> responseList = skuDetails.getStringArrayList("DETAILS_LIST"); for (String thisResponse : responseList) { JSONObject object = new JSONObject(thisResponse); String sku = object.getString("productId"); String price = object.getString("price"); if (sku.equals("premiumUpgrade")) mPremiumUpgradePrice = price; else if (sku.equals("gas")) mGasPrice = price; } }
인앱 상품 구매
(요청)
Bundle buyIntentBundle = mService.getBuyIntent(3, getPackageName(), sku, "inapp", "bGoa+V7g/yqDXvKRqq+JTFn4uQZbPiQJo4pf9RzJ");
PendingIntent pendingIntent = buyIntentBundle.getParcelable("BUY_INTENT");
startIntentSenderForResult(pendingIntent.getIntentSender(), 1001, new Intent(), Integer.valueOf(0), Integer.valueOf(0), Integer.valueOf(0));(응답)
@Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == 1001) { int responseCode = data.getIntExtra("RESPONSE_CODE", 0); String purchaseData = data.getStringExtra("INAPP_PURCHASE_DATA"); String dataSignature = data.getStringExtra("INAPP_DATA_SIGNATURE"); if (resultCode == RESULT_OK) { try { JSONObject jo = new JSONObject(purchaseData); String sku = jo.getString("productId"); alert("You have bought the " + sku + ". Excellent choice, adventurer!"); } catch (JSONException e) { alert("Failed to parse purchase data."); e.printStackTrace(); } } } }(구매 정보)
'{ "orderId":"GPA.1234-5678-9012-34567", "packageName":"com.example.app", "productId":"exampleSku", "purchaseTime":1345678900000, "purchaseState":0, "developerPayload":"bGoa+V7g/yqDXvKRqq+JTFn4uQZbPiQJo4pf9RzJ", "purchaseToken":"opaque-token-up-to-1000-characters" }'
* 구독에 대한 구매 흐름은 유사하지만 상품 유형이 subs로 설정해야 함.
(요청)
Bundle bundle = mService.getBuyIntent(3, "com.example.myapp", MY_SKU, "subs", developerPayload); PendingIntent pendingIntent = bundle.getParcelable(RESPONSE_BUY_INTENT); if (bundle.getInt(RESPONSE_CODE) == BILLING_RESPONSE_RESULT_OK) { // Start purchase flow (this brings up the Google Play UI). // Result will be delivered through onActivityResult(). startIntentSenderForResult(pendingIntent, RC_BUY, new Intent(), Integer.valueOf(0), Integer.valueOf(0), Integer.valueOf(0)); }(구매 확인)
Bundle activeSubs = mService.getPurchases(3, "com.example.myapp", "subs", continueToken);
* Transaction 무결성을 보장하기 위해 Google Play는 구매 응답 데이터를 포함한 JSON 문자열에 서명함.
[인앱 상품 소비]
구매한 상품은 소유된 상태로 간주되고 소유된 상태는 Google Play에서 다시 구매할 수 없음. 하지만 소유한 상품을 소비 하면 구매 데이터가 삭제 되고 소유되지 않는 상태로 변경됨.
int response = mService.consumePurchase(3, getPackageName(), token);
[구독]
[홍보]
[보안 및 디자인]
[테스트]
[관리]
피드 구독하기:
글 (Atom)