Regular Motion

개발자가 상팔자

AWS S3 Transfer Accelerator

AWS의 S3는 Static한 파일을 저장/배포하기에 매우 훌륭한 저장소지만, 버킷의 Region이 접속지역과 멀리 떨어진 경우 응답시간과 전송시간 모두 사용자들의 인내심을 테스트하게 만드는 단점이 있다. 사실 다운로드의 경우 CDN(CloudFront)을 이용해 개선 할 수 있지만, 업로드는 방법이 없다.

현재 회사에서 운영중인 서비스는 유럽에 위치한 주요 고객(이라 적고 갑이라 읽는다)을 위해 대부분의 버킷을 eu-west-1에 위치시키고 있다.  eu-west-1의 버킷의 경우 100KB의 파일을 업로드/다운로드하는데 1~3초, 5MB 정도의 파일은 수십초가 소요된다.  (편리함의 대가로 시간을 지불하고 있었던 것이다).

매일같이 시간을 지불하고 있던 어느날, S3에 Transfer Acceleration을 활용하면 적게는 2~3배에서 많게는 10배 정도 업로드/다운로드 속도가 빨라진다는 글을 접하게 됐다.  평소 의심이 많고, 두눈으로 보기전엔 믿지 않는 분들은 AWS에서 제공하는 속도비교 페이지에 접속해보시기 바란다.  7번 정도 확인을 해봤는데 eu-west-1의 경우 6~21배 정도 속도차이가 있었다.

이제 Transfer Acceleration의 효과를 봤으니, 1) 속도 개선이 어떻게 이루어지고 2) 적용 방법은 어떻게 되며 가장 중요한 그래서 3) 얼마를 더 내야 되는지 알아보자.

Overview

공식 설명은 ‘S3 Transfer Acceleration은 AWS S3로 데이터를 전송 할 때, AWS의 Edge 인프라와 네트워크 프로토콜을 최적화하여 전송 속도를 개선하는 기능’ 이라고 되어 있습니다.

사용자가 S3 Bucket에 데이터를 전송 할 때, 직접 Origin 서버로 전송하는게 아니라 AWS의 Edge 인프라를 활용하여 사용자는 가장 가까운 Edge 서버로 데이터를 전송하고, Edge 서버에서 Origin 서버로 최적화된 네트워크 프로토콜과 전용회선을 활용하여 데이터를 전송함으로써 속도를 개선합니다. 그림으로 표현하면 아래와 같습니다.

Before

After

자칫 복잡해 보일 수 있는 아래의 방식이 5~10배 이상 빠르다는건 Edge 서버와 S3 사이의 네트워크 프로토콜과 데이터 이동이 최적화되었다는 반증일겁니다. 실제로 Edge 인프라를 활용하기 위해 Transfer Acceleration을 사용하는 요청은 Endpoint가 기존과 다릅니다.

  • Original Endpoint: https://{your-bucket}.s3.amazonaws.com/
  • Accelerated Endpoint: https://{your-bucket}.s3-accelerate.amazonaws.com/

 

버킷에서 Transfer Acceleration 설정

Transfer Acceleration은 Bucket 레벨에서 적용할 수 있는 기능이기 때문에, 적용을 원하는 버킷의 속성에서 Transfer Acceleration을 활성화(Enable)해주면 됩니다.  활성화 방법은 아래 세가지 방법 중 평소 본인이 흠모하던 방법으로 하시면 됩니다. 자세한 방법은 링크를 참고하시는게 글을 쓰는 사람과 읽는 사람 모두의 정신건강에 이로울 것 같아 링크로 대체합니다.

Transfer Acceleration 활용

아쉽게도, 버킷에서 Transfer Acceleration 속성을 활성화하는 것으로 갑자기 빨라지는 건 아니고, 버킷에 파일을 업로드/다운로드를 하는 부분도 약간의 수정을 해야 합니다. SDK를 사용하고 있었다면 Flag를 활성화하는 수준이고, REST API를 사용하고 있었다면 Endpoint를 대체하는 수준으로 가능합니다. (역시나 우리 모두의 정신 건강을 위해 링크로 대체합니다)

Transfer Acceleration 비용

저장 비용에는 변화가 없으며, Transfer Acceleration을 이용한 전송에 추가 비용이 발생합니다. GB당 추가로 $0.04가 부과됩니다. (스벅에서 카페라떼 한잔을 참으면 약 100GB를 빠르게 전송 할 수 있습니다. 반대로 100GB를 느긋하게 전송하면 카페라떼를 한잔 사먹을 수 있습니다)

Benchmark Test

평소 의심이 많던 저는 AWS의 속도 비교 페이지를 온전히 믿지 못하고, 직접 벤치마크 테스트를 해봤습니다. 아래 표는 eu-west-1 버킷에 CLI를 이용하여 파일을 업로드/다운로드한 결과입니다. 파일의 크기가 크면 클수록 속도 개선이 드라마틱하게 이루어짐을 알 수 있습니다. 15MB 정도되는 파일의 경우 평균 43초 정도 소요되던 다운로드 속도가 4.7초로 개선된 것을 확인 할 수 있습니다.

 

주의사항
  • 설정 후 반영까지 최대 30분정도가 소요 될 수 있습니다.

Common Design Patterns for Android

이 글은 https://www.raywenderlich.com에 소개된 글을 저자의 동의를 얻고 번역한 것임을 알려드립니다.

개발자로서 일하며 행복을 유지하기 위해서는 고객과 사장님도 만족시켜야 하지만, 미래의 나 또한 만족시켜야 한다.  (행복한 닭이 더 맛있는 달걀을 낳는다고 하지 않던가)

미래의 나는 과거의 내가 작성한 코드를 이어받게 될 것이고, 아마 왜 이렇게 코드를 작성했는지 궁금해 할 것이다.  헷갈리는 주석을 왕창 달아 놓는 것보다, 많이 사용되는 디자인 패턴을 적용하여 코드를 읽기 쉽고, 구조화 시켜 놓는게 미래의 나를 위해 훨씬 나은 방법일 것이다.

이 글은 안드로이드 개발을 하며 많이 사용하는 디자인 패턴을 소개 할 것이다. 디자인 패턴이란 일반적인 소프트웨어 개발시 마주 할 수 있는 문제들을 해결 할 수 있는 재사용 가능한 해결책이라고 할 수 있다. 이 글에서는 많은 디자인 패턴을 소개하지는 않을 예정이지만, 시작점으로 좋은 글이 될 것이라고 생각한다.

Getting Started

“동일한 수정을 여러 곳에 적용해야 할 부분이 이 프로젝트에 있나요?” – Future You

미래의 당신은 아마 추측하거나 추론하며 프로젝트의 의존성을 찾는 시간을 최소화하고 싶을 것이다. 그러기 위해 프로젝트는 최대한 재사용 가능하며, 읽기도 쉽고, 이해하기 쉬워야 할 것이다.  하나의 클래스부터 전체 프로젝트에 이르기까지 위 목표를 달성하기 위해, 패턴을 다음의 카테고리로 나눌 수 있다.

  • Creational patterns: how you create objects.
  • Structural patterns: how you compose objects.
  • Behavioral patterns: how you coordinate object interactions.

당신은 아마 각각의 패턴을 지칭하는 이름을 모른채 몇 종류의 패턴들은 사용하고 있을지도 모른다. 이제 각각의 카테고리에 어떤 패턴이 있으며 실제로 Android에 어떻게 구현/적용 할 수 있는지 알아보자.

Creational

  • Builder
  • Dependency Injection
  • Singleton

Structural

  • Adapter
  • Facade

Behavioral

  • Command
  • Observer
  • Model View Controller
  • Model View ViewModel

Creational Patterns

“복잡한 오브젝트가 여러곳에서 필요할 때는 어떻게 하죠?” – Future You

미래의 당신은 위 물음에 “동일한 코드가 필요할 때마다 Copy and Paste하면 되잖아요?” 라는 말을 듣고 싶지는 않을 것이다.  대신 Creational Pattern으로 오브젝트의 생성을 단순하게 유지하고 쉽게 반복 가능하도록 만들 수 있습니다.

Builder

단골 샌드위치 가게의 주문서에 빵의 종류와 재료 그리고 양념을 펜으로 선택한다. 주문서의 이름이 “Build my own”이지만 실제로 샌드위치를 직접 만들지는 않고 입맛에 맞게 각각의 재료들을 선택한 뒤 직원에게 넘겨 줄 뿐이다.

이와 비슷하게 Builder pattern은 Object의 복잡한 생성과정(빵을 익히고 자르고, 재료를 넣고, … )을 Object(샌드위치)에서 분리한 것이다. 이와 같은 방법을 통해 동일한 프로세스로 다른 Object(샌드위치)를 쉽게 만들어 낼 수 있다.

Android의 AlertDialog.Builder와 같은 오브젝트에 Builder pattern이 사용된다.

위의 AlertDialog를 Build하는 과정은 단계적으로 진행되며, AlertDialog의 요소 중 수정하고 싶은 부분만 수정 할 수 있도록 해준다.  AlertDialog.Builder 문서를 보면, Alert을 만들 때 매우 제한적인 요소만 설정 할 수 있다는 것을 알 수 있다.

위 코드는 아래와 같은 Alert을 생성한다.

please_use_the_spicy_mustard

Dependency Injection

Dependency Injection은 빌트인 가구가 갖춰진 아파트로 이사하는 것과 비슷하다. 필요한게 이미 있기 때문에; IKEA 홈페이지에 접속해서 가구를 주문하고 배달을 할 필요가 없다.

소프트웨어 측면에서 보자면, Dependency Injection은 새로운 Object를 생성하는 시점에 필요한 Object들을 제공해준다. 따라서 새로 생성되는 Object는 필요한 Object들을 만들 필요가 없다.

안드로이드에서는 앱의 다양한 Activity 또는 Fragment에서 Network Client나 Image Loader, SharedPreferences와 같은 다양한 Object들에 접근 할 필요가 있다. Dependency Injection을 활용하면 이러한 Object들을 Activity나 Fragment들에 Inject 한 뒤, 필요한 시점에 바로 사용 할 수 있도록 해준다.

Dagger 2는 Google과 Square가 협업하여 개발한 Framework으로, Android에서 가장 유명한 Dependency Injection Framework이다. 클래스에 @Module 어노테이션을 추가한 뒤, @Provides 어노테이션으로 필요한 모듈을 제공하도록 할 수 있다.

위 모듈은 Object 생성과 필요한 설정을 수행한다. 규모가 있는 앱을 위한 Best practice로는, 여러 모듈을 함수로 구분하여 생성 할 수도 있다. Component interface를 만든 뒤, Inject가 필요한 모듈과 클래스를 나열한다.

Component는 dependency들이 어디서 와야(module coming from) 되고 어디로 가야(injection point) 되는지를 판단하여 묶어 주는 역할을 한다.

마지막으로 @Inject 어노테이션을 사용해서 Dependency가 필요한 곳에 삽입 할 수 있다.

* Dagger에 대한 간소화된 개요이기 때문에, 자세한 내용은 Dagger 문서를 참조하기 바란다.

Singleton

Singleton 패턴은 클래스의 인스턴스가 오직 하나만 생성되도록 해준다. 모델링하는 Object가 실제로 단 하나의 인스턴스만 갖을 때, Singleton 패턴은 잘 동작한다.

위 ExampleSingleton 클래스는 객체의 생성자를 static 함수인 getInstance() 함수 안으로 숨겨서, 객체가 하나만 생성되도록 보장한다. Singleton 객체에 접근이 필요할 때는 아래와 같이 getInstance() 함수로 접근 할 수 있다.

Singleton은 이해하기 가장 쉬운 패턴 중 하나지만, 오/남용되기 가장 쉬운 패턴이기도 하다.  복수의 객체에서 동일한 Singleton 인스턴스에 접근 할 수 있기 때문에 예상치 못한 Side Effect가 발생하기 쉽고, 원인을 파악하기 어려운 경우도 많다.  따라서 사용하기 전에 패턴을 정확히 이해하고 사용하는 것이 중요하다.

 

Structural Patterns

미래의 당신은 전형적인 작업(typical task)을 수행하는 클래스와 오브젝트들을 익숙한 형태로 정리 할 수 있도록 도와준 Structural Pattern을 의심의 여지없이 고마워 할 것이다. AdapterFacade는 안드로이드에서 가장 많이 사용되는 패턴이다.

Adapter

Apollo 13에서 엔지니어들이 네모난 마개를 ‘Round Hole’에 맞추는 유명한 장면이 있다. 이 장면은 Adapter의 역할을 은유적으로 보여준다. Adapter 패턴은 Class의 Interface를 변환하여 사용자가 예상하는 다른 Class의 Interface에 맞출 수 있도록 해준다.
(By converting the interface of a class into another interface the client expects).

당신 앱의 비즈니스 로직은 제품이 될수도, 사용자 또는 건조틀이 될 수도 있다. 그게 뭐가됐든, RecyclerView는 모든 Android App에서 공통으로 사용되는 ‘Round Hole’ 이다.

RecyclerView는 Star Trek의 에피소드를 하나도 본적이 없기 때문에 Tribble이 뭔지 모른다.  하지만 데이터를 처리하고, ViewHolder에 데이터를 올바르게 전달해주는 Adapter의 역할을 한다.

Facade

Facade 패턴은 인터페이스들의 모음을 사용하기 쉽게 하기 위한 Higher-level 인터페이스를 제공한다. 아래 다이어그램은 이를 더 자세히 설명해준다.

만약 Activity에서 도서 목록이 필요하다면, 내부 저장소나 캐시 그리고 API 클라이언트의 내부 로직에 대한 이해 없이도 요청 할 수 있어야 한다.  이는 Activity와 Fragment의 코드를 깨끗하고 간결하게 유지하는 것을 넘어 Activity에 영향을 주지 않고, API 구현에 대한 변경사항을 적용 할 수 있도록 해준다.

SquareOne의 Retrofit은 Facade 패턴을 구현할 수 있도록 도와주는 Open Source Android 라이브러리다. API data를 제공하는 Interface를 아래와 같이 생성하면,

Client에서는 listBooks()을 호출하는 것 만으로 도서 목록을 얻어 올 수 있다.  RequestInterceptor 또는 OkClient의 설정을 수정(customize)하는 것 만으로 Client의 변경없이 캐시 동작 방식을 제어 할 수 있다.

Behavioral Patterns

Behavioral 패턴은 앱의 기능에 책임을 부여 할 수 있도록 해준다. Behavioral 패턴을 통해 프로젝트의 구조나 설계를 파악하는데 도움을 얻을 수 있다. 이 패턴의 Scope는 두 객체간의 관계에서 부터 전체 어플리케이션의 구조에 이르기까지 다양하게 나뉜다. 다양한 Behavioral 패턴이 하나의 앱에 사용되는게 일반적이다.

Command

인도 음식점에서 ‘Saag Paneer’를 주문했을 때, 음식이 만들어지는 과정은 알 필요가 없다. 종업원에게 주문을 알려주기만 하면 된다.

이와 비슷하게 Command 패턴은 Request를 수신하는 곳에 대한 정보없이 발행 할 수 있도록 해준다.

Greenrobot의 EventBus는 Command 패턴이 아래와 같이 동작할 수 있도록 해주는 Android Framework다.

EventBus-Publish-Subscribe

EventBus의 실제 동작방식은 링크를 참고하기 바란다.

Observer

Observer 패턴은 Objects간의 1 대 多 의존성을 정의 한 뒤, Object의 상태가 변경됐을 때, 이에 의존적인 Object들에게 내용이 전달되고, 자동으로 갱신된다.

Observer 패턴은 활용도가 높아, API Call이나 사용자 입력에 대한 반응과 같이 종료시간을 예측하기 어려운 작업에는 모두 적용 할 수 있다.

RxAndroid Framework은 아래와 같은 방식으로 Observer 패턴을 앱에 적용 할 수 있게 해준다.

 

Getting Literal with Template Strings.

최근 개발하고 있는 기능 중, JavaScript에서 XML String을 생성하는 부분이 있다.  해당 코드는 Template화된 XML String에서, 필요한 값들을 replace하는 방식으로 구현되어 있었다.
문제는 Template에 replace 해야 할 값들이 많을 수록 코드의 미적인 부분과 성능적인 부분이 함께 추락하는 방식이었다.
아래의 코드는 위의 예시와 72% 정도 유사하다.

생성해야 할 XML Template을 String으로 선언한 뒤, 변수로 치환되어야 할 값들을 String.prototype.replace()를 통해 치환하는 구조다.  쉽고, 나름 직관적이다.  사실 크게 문제가 있는 코드라고 보기는 어렵다. 하지만 위에서 언급했듯 치환되어야 할 값들이 늘어나면 replace chaining이 늘어나며 뭔가 문제가 생길 것 같은 불안감을 내포하는 코드다.

그래서 위 코드를 깔끔하게 개선하고 싶어 약간의 조사를 해보니 ES6의 Template Strings을 활용하면 매우/훨씬 깔끔한 코드로 수정 가능 할 것 같았다.  (정말 약간의 조사로 알게되어 애초에 왜 저렇게 구현했나 자괴감이 들었다)

실제로 위 코드를 ES6의 Template Strings을 활용하여 수정한 코드는 아래와 같다.

문장을 감싸는 부호를 single quotes(‘)에서 back-ticks(´)으로 대체했고, 값들을 치환하는 replace() chaining 구문이 사라졌다. (JavaScript 엔진에 의해 자동으로 치환된다는 의미다.)
코드가 간결해졌다는 것은 시력이 0.2 이상인 사람은 모두 육안으로 확인 가능하고, 성능이 빨라졌다는 것은 간단한 Benchmark를 통해 증명 가능하다.  (https://jsfiddle.net/6xnutcsd/)
* 위의 예제는 크롬에서 7~10배 정도의 성능 향상이 있으며, 치환되어야 할 값들이 많을수록 성능 개선 효과도 크게 나타났다.

 

예제로 글을 시작했으니, 이제 문법과 활용 범위에 대해 알아보자.
(글 끝부분의 Reference 링크를 참고하시는게 더 낫다)

Syntax
Template String은 일반적으로 String을 감쌀 때 사용하던 single quote(‘)나 double quote(“) 대신 back-ticks(`)을 사용하면된다.  쉽다;


String Substitution
문자열에 JavaScript Expression을 삽입 할 수 있는 String Substitution은 Template String의 가장 큰 장점이라고 할 수 있다. 심지어, 거의 모든 JavaScript Expression이 허용되며,  치환/처리가 필요한 Expression을 중괄호{}로 감싼 뒤, 앞에 달러($)를 붙여주기만 하면 된다. (아, 이놈의 자본주의, 그놈의 돈돈돈)

Expression에는 단순히 변수의 이름이나, 간단한 수식은 물론이고, Instance의 property나 method등 일반적인 JavaScript Expression 대부분 허용된다. (개발자한테 참 좋은데, 설명할 방법이 없다.)

Tagged Templates
Tagged Templates은 함수를 이용하여 Template String의 출력을 변형하기 위한 목적으로 사용 할 수 있다. 사용법은 함수의 이름 뒤에 Template String을 선언하면 된다. 그러면 함수의 첫번째 인자로 Template String의 문자열이 Array 형태로 전달되고, Expression이 순서대로 함수의 인자로 전달된다.

상상력이 미천한건지 아직까지 Tagged Templates의 활용할 코드는 발견하지 못했다.

 

References

 

Should URL be case sensitive?

회사에서 회의 중 URL이 Case Sensitive하게 처리되어야 하는지에 대한 사실 확인이 필요하여 이를 찾아본 과정의 기록입니다.

결론부터 말하면 URL은 Case Sensitive 합니다.
(결론부터 알려드렸지만 끝까지 읽어주세요. 광고도 없는 걸요;)

아주 조금만 더 들어가서 우리가 URL이라고 말하는 건 크게 6가지 요소로 나눌 수 있습니다.

RFC 7230에 따르면, 위 구성요소 중 Scheme와 Host를 제외한 요소들은 Case Sensitive하게 처리되어야 합니다.
(Port는 숫자로만 구성되니 논외로 합시다)

 

시작부터 비교적 명확하게 결론이 났습니다.  Scheme와 Host를 제외한 요소들은 Case Sensitive하게 처리되어야 한다.

즉,  https://www.google.com/HTTPS://WWW.GOOGLE.COM/은 완전히 동일한 결과를 보장하지만,  https://www.google.co.kr/search?query=new와 https://www.google.co.kr/search?query=NEW는 다른 URL로 볼 수 있고, 실제로 검색결과도 미세하게 다릅니다.

 

어허,  근데 실제로 대부분의 사람들이 URL을 입력 할 때 대/소문자를 의미있게 구분해서 사용하고 있을까? 라고 자문해보면  ‘아니오‘라고 말 할 수 있습니다. 대부분의 사람들은 소문자만으로 URL을 입력하고 있고, URL에 대문자가 섞여 있다면 아마 Caps lock을 의심해봐야 합니다.

RFC 규약의 scheme와 host를 제외한 요소는 Case Sensitive하게 처리돼야 한다는 사실과는 별개로 실제 URL에 대소문자를 의미있게 섞어서 사용하는 사람이 거의 없다고 할 때,  HTTP://WWW.EXAMPLE.COM/LIST로 접속한 사용자에게 http://www.example.com/list의 결과화면을 보여주지 않을 이유가 없습니다.

실제로 많은 서비스들에서는 Scheme와 Host를 제외한 요소에 대해서도 Case Insensitive하게 URL을 처리하고 있습니다.  믿지 못하는 분은 아래 2개의 링크를 클릭해보시길 바랍니다. 동일한 페이지를 나타내는 걸 알 수 있습니다.

http://stackoverflow.com/questions/7996919/should-url-be-case-sensitive

HTTP://STACKOVERFLOW.COM/QUESTIONS/7996919/SHOULD-URL-BE-CASE-SENSITIVE

 

다시 처음으로 돌아가서 그래서 URL은 Case Sensitive한가? 또는 Case Sensitive하게 처리되어야 하는가?

RFC에 따르면 Scheme와 Host를 제외한 요소들은 Case Sensitive하게 처리되어야 한다. 허나; 서비스에서 Path와 Query, Fragment의 대소문자 구분이 크게 의미가 없을땐 알아서 잘 하자잉.

 

References

 

2017 목표

역시 사람이 목표가 있어야 되는 것 같아서 새해를 맞아 몇가지 목표를 세움.

  • 업무 목표
    • Spindle Android Viewer재설계 + 리펙토링: 4년간 여러가지 기능을 추가하는 과정에서 Viewer가 많이 낙후되고야 말았다. 외관은 크게 낙후되지 않았으나 내부 구조는 조금 낙후된 것 같다. 개인적으로는 신경을 쓰면서 기능을 추가했지만 시간에 장사가 없는 것 같다. 바닷가 바람에 녹슨 배처럼 정비가 필요한 시점이다.
      재사용 가능한 모듈은 살리되, 설계부터 다시하는 방향으로 Refactoring이 필요한 시점이 된 것 같다.
      시간이 2~3달 정도 소요될 것 같은데 어떻게 시간을 만드나;;  그래도 진행하면 2~3달은 꽤나 재미있게 작업 할 수 있을 것 같고, 개선되는 부분도 아마 매우 선명할 것 같다.
    • Tablet에 최적화된 eBook 포맷 기획 및 개발: 현재의 포맷이 갖는 한계는 비교적 명확한 것 같다.
      1) 일반적으로 책의 물리적인 크기가 태블릿 화면보다 크다.  2) 책의 물리적인 크기는 대부분의 사람이 편하게 볼 수 있는 크기로 만들어졌을 확률이 높다.  1)과 2)가 참일 때 책의 비율을 유지하면서, 태블릿에서 컨텐츠를 보여주려면 책이 원하는 크기보다 좀 작게 보여질 확률이 높다.  물론 Zoom in/out은 가능하지만 Default 값은 중요하지 않은가. 회사가 보유한 좋은 책들의 가치를 100% 프린트북 대비 150%를 끌어낼 수 있는 새로운 포맷을 개발해야 될 시점이 점점 다가오는 것 같다. 기기 사양은 이미 충분히 올라왔다.

 

  • 개발자로서의 목표
    • AWS 공인 솔루션 아키텍트 자격증 취득: 현재 회사의 이점을 살려, AWS 솔루션 아키텍트 공부를 하여 자격증을 취득해보자. 3~4달 정도 공부를 해야 될 것 같은데 앞으로 도움이 될 것 같은 몇 안되는 자격증으로 보이니 꼭 공부해서 상반기내 취득을 목표로. 취득하는 과정에서도 도움이 많이 될 것 같고, 자격증을 보유한 것으로도 도움이 될 것 같다.
    • Radio inn: 올해에는 다운로드 수 보다는 기능 추가에 목표를 두고 개발
      1) 로그인 / 로그아웃 / 북마크 팟캐스트 계정 동기화
      2) 친구 또는 다른 사용자가 북마크한 팟캐스트 열람
      3) 취향 기반 팟캐스트 추천 서비스
      4) 팟캐스트 별점 / 에피소드 별점
    • 블로깅 12개: AWS 자격증 준비 및 Radio inn 개선 과정에서 배운 것들을 꾸준히 포스팅. 양보다는 질. 허나 최소 12개 정도는 블로깅 할 수 있도록.
    • Git을 자유롭게: 아직도 Git이 어색할때가 있다. git을 자유롭게 사용할 수 있도록.

 

  • 개인 목표
    • 82kg까지 다이어트: 문득, 예전 사진을 보다 거울 속 나와 너무 차이가 나는 것 같아 급하게 목표에 추가하게 되었다. 7kg을 빼보자. 82kg까지 다이어트.
    • 어진이와 분기별 1회 둘이서 여행: 초등학교 들어가면 아빠보다 친구들을 더 좋아한다고 하던데, 아마 올해가 어진이와 가장 잼있게 보낼 수 있는 시기 일 것 같다. 올해는 꼭 같이 여행을 많이 다녀야겠다. 캠핑이면 가장 좋겠지만, 캠핑이 아니더라도 1) 물놀이 할 수 있는 곳 2) 박물관 / 미술관 위주로 딸과 많이 여행을 다녀야겠다.

 

« Older posts

© 2017 Regular Motion

Theme by Anders NorenUp ↑