스토리 홈

인터뷰

피드

뉴스

조회수 2048

다양한 형태를 지원하는 리스트 UI, 잘 그리고 계신가요?

대략 1년 반 전, 5.0 롤리팝과 함께 나타난 RecyclerView. ListView 를 이용할 때 아주 기초적이고 정석적인 개념으로 사용되던 ViewHolder pattern 을 반 강제화? 하면서 동시에 성능까지 개선한 ListView 의 개량버전.앱 시장이 활성화되면서 한 가지 타입의 뷰만 반복적으로 보여주는 단순한 구성보다는 다양한 타입의 뷰를 보여주는 앱들이 많아지고 보편화 된 시점에 이것을 구현하기 위한 Adapter.getView 메소드는 혼돈.chaos 가 되었지요. 가독성을 높일만한 나름대로의 시도를 해보고 있을 때, RecyclerView 가 갑툭튀 했고 이걸 이용하면 원하는 만큼의 많은 타입의 뷰를 “가독성 좋게 만들어 볼 수 있겠다” 라는 생각이 들었습니다.그래서 RecyclerView.Adapter 를 상속 받아 다양한 타입의 뷰를 바인딩 할 수 있게 도와주는 헬퍼 클래스, MultiItemAdapter 라는 것을 만들어 보게 됐습니다. 구 회사 프로덕트에 적용해보기도 하고, 개인 프로젝트에 넣어보기도 하고, 토스랩에서 서비스하고 있는 “잔디”에 녹여내보기도 했는데 나쁘지 않은 느낌이들어 그 과정을 공유하고 많은 분들께 피드백도 받고 싶습니다. 또, 어떻게 더 잘 활용하고 계신지 여쭙고 싶습니다.RecyclerView.Adapter 의 이해를 위해 단순단순하게 만들어보자public class BasicAdapter extends RecyclerView.Adapter { private List mItems = new ArrayList<>(); @Override public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View itemView = LayoutInflater.from(parent.getContext()) .inflate(android.R.layout.simple_list_item_1, parent, false); return new MyViewHolder(itemView); } @Override public void onBindViewHolder(MyViewHolder holder, int position) { holder.mTextView.setText(mItems.get(position)); } class MyViewHolder extends RecyclerView.ViewHolder { private TextView mTextView; public MyViewHolder(View itemView) { super(itemView); mTextView = (TextView) itemView.findViewById(android.R.id.text1); } } ... 이런 식으로 구현하면 되는군, 하지만 내가 최종적으로 원하는 건 다양한 ViewHolder 를 다뤄야 되는 건데 ViewHolder 가 많아지는 경우 inner class 는 쓰면 안되겠다! ViewHolder 들은 따로 패키지 만들어서 관리하자. 음 근데 ViewHolder 를 구성하고 난 다음 어떻게 그려지는 지에 대해 궁금하면 다시 어댑터를 찾아가야 되고, 반대로 어댑터에서 ViewHolder 내 구성요소가 어떻게 생겼는지 궁금하면 다시 ViewHolder 찾아가서 뒤져봐야되는 군. 이건 비효율 적인 것 같다. ViewHolder에 뷰를 그리는 메소드를 하나 만들자. 아 기왕이면 추상화된 클래스를 만들어 돌려돌려 쓰자. 하나 더 Generic 을 사용하자.public abstract class BaseViewHolder extends RecyclerView.ViewHolder { public BaseViewHolder(View itemView) { super(itemView); } public abstract void onBindView(ITEM item); } 뷰를 그리는데 쓰이는 객체는 Generic 을 이용하면 ViewHolder 안에서 그리는 작업 또한 해결이 가능하겠군! 이걸 이용해서 다시 만들어보자.public class MyViewHolder extends BaseViewHolder { private TextView mTextView; public MyViewHolder(View itemView) { super(itemView); mTextView = (TextView) itemView.findViewById(android.R.id.text1); } @Override public void onBindView(String item) { mTextView.setText(item); } } ... public class BaseAdapter extends RecyclerView.Adapter { private List mItems = new ArrayList<>(); @Override public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View itemView = LayoutInflater.from(parent.getContext()) .inflate(android.R.layout.simple_list_item_1, parent, false); return new MyViewHolder(itemView); } @Override public void onBindViewHolder(MyViewHolder holder, int position) { holder.onBindView(mItems.get(position)); } public void setItems(List items) { mItems.clear(); mItems.addAll(items); } @Override public int getItemCount() { return mItems.size(); } } 음 원하는 모양새다. 근데 이제 Adapter 에선 ViewHolder 에 들어갈 layout 이 어떤 건지 관심꺼도 되겠네. 게다가 ViewHolder 에서 layout 궁금하면 다시 또 찾아와야 되는게 문제다. 좀 더 명시적인 방법으로 Factory method 로 생성자를 제한해보자. RecyclerView.ViewHolder 는 View 를 가지는 생성자가 강제되니 이렇게 바꾸자.public static MyViewHolder newInstance(ViewGroup parent) { View itemView = LayoutInflater.from(parent.getContext()) .inflate(android.R.layout.simple_list_item_1, parent, false); return new MyViewHolder(itemView); } private MyViewHolder(View itemView) { super(itemView); mTextView = (TextView) itemView.findViewById(android.R.id.text1); } 이렇게 하면 어떤 layout 을 다루고 있는지도 금방 알 수 있겠다. 이 정도만 되도 구색을 다 갖춘듯하니 이 느낌으로 다양한 타입의 뷰들을 다뤄보자.public class BasicMultiTypeAdapter extends RecyclerView.Adapter { public static final int VIEW_TYPE_A = 0; public static final int VIEW_TYPE_B = 1; private List mItems = new ArrayList<>(); @Override public BaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { if (viewType == VIEW_TYPE_A) { return AViewHolder.newInstance(parent); } else { return BViewHolder.newInstance(parent); } } @Override public void onBindViewHolder(BaseViewHolder holder, int position) { holder.onBindView(mItems.get(position)); } public void setItems(List items) { mItems.clear(); mItems.addAll(items); } @Override public int getItemCount() { return mItems.size(); } @Override public int getItemViewType(int position) { if (position % 2 == 0) { return VIEW_TYPE_A; } else { return VIEW_TYPE_B; } } } 음 깔끔하긴 하다. 근데 getItemViewType 이 스크롤 할 때마다 불릴 텐데, 분기도 많고 연산이 생겼을 때 스크롤 속도에 괜한 영향을 줄 듯? view type 을 차라리 미리 가지고 있게 만들자. 또! 가만보니 한 타입의 객체를 이용해서 다른 스타일로 뷰를 보여줄 뿐이었네. 이것도 여러가지 객체를 담을 수 있게 만들어야지.뷰를 그릴 대상이 될 객체랑 타입을 가지는 Wrapper class 를 만들어서 해결하자. 이러면 Adapter.onBindViewHolder 랑 Adapter.getItemViewType 도 해결이 되겠군.public abstract class MultiItemAdapter extends RecyclerView.Adapter { private List mRows = new ArrayList<>(); @SuppressWarnings("unchecked") @Override public void onBindViewHolder(BaseViewHolder holder, int position) { holder.onBindView(getItem(position)); } @SuppressWarnings("unchecked") public ITEM getItem(int position) { return (ITEM) mRows.get(position).getItem(); } public void setRows(List mRows) { mRows.clear(); mRows.addAll(mRows); } @Override public int getItemCount() { return mRows.size(); } @Override public int getItemViewType(int position) { return mRows.get(position).getItemViewType(); } public static class Row { private ITEM item; private int itemViewType; private Row(ITEM item, int itemViewType) { this.item = item; this.itemViewType = itemViewType; } public static Row create(T item, int itemViewType) { return new Row<>(item, itemViewType); } public ITEM getItem() { return item; } public int getItemViewType() { return itemViewType; } } } MultiItemAdapter 완성.네, 저는 이렇게 만들어서 1년 반 정도 필요한 부분(복잡해 질만한 부분)에 이 클래스를 상속받아 구현했습니다. 사용방법을 예로들어 데이터베이스나 서버로부터 긁어온 아이템들을 타입에 따라 A, B로 나눠서 보워줘야 한다면,// MutiItemAdapter 구현 public class AdvancedItemAdapter extends MultiItemAdapter { public static final int VIEW_TYPE_A = 0; public static final int VIEW_TYPE_B = 1; @Override public BaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { if (viewType == VIEW_TYPE_A) { return AViewHolder.newInstance(parent); } else { return BViewHolder.newInstance(parent); } } } // Activity 나 Fragment 등 view 요소에서 ListAdapter item setting. public void setItems(List items) { List rows = new ArrayList<>(); for (int i = 0; i < items xss=removed>이렇게 해주면 됩니다. 그런데 위 사용방법을 보면 추가적인 새로운 타입(Row)의 List 와 반복문을 돌려야 된다는 것이 단점으로 보이는데요. 그럼 이 클래스를 사용하지 않고 직접 구현한 결과를 좀 볼까요?public class NormalItemAdapter extends RecyclerView.Adapter { public static final int VIEW_TYPE_A = 0; public static final int VIEW_TYPE_B = 1; private List mItems = new ArrayList<>(); @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { if (viewType == VIEW_TYPE_A) { View itemView = LayoutInflater.from(parent.getContext()) .inflate(android.R.layout.simple_list_item_1, parent, false); return new AViewHolder(itemView); } else { View itemView = LayoutInflater.from(parent.getContext()) .inflate(android.R.layout.simple_list_item_1, parent, false); return new BViewHolder(itemView); } } @Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { if (holder instanceof AViewHolder) { Item item = getItem(position); ((AViewHolder) holder).getTextView().setText(item.getName()); } else { ((BViewHolder) holder).getTextView().setText("I am B."); } } private Item getItem(int position) { return mItems.get(position); } public void setItems(List items) { mItems.clear(); mItems.addAll(items); } @Override public int getItemViewType(int position) { if (getItem(position).getType().equals(Item.ITEM_TYPE_A)) { return VIEW_TYPE_A; } else { return VIEW_TYPE_B; } } @Override public int getItemCount() { return mItems.size(); } } 뭐, 나쁘진 않습니다. 이 정도 수준으로 개발이 끝나도 되고 추가적인 확장이 필요하지 않아보인다면 굳이 MultiItemAdapter 를 쓸 필요가 없습니다.중요성을 가지는 리스트 위주의 화면에서 위와 같이 개발된다면 당장 보이는 제 불만은 onCreateViewHolder, onBindViewHolder 계속해서 분기가 들어가게 되고 getItemViewType 에서는 계속 해서 List 데이터에 접근해야 한다는 것입니다. 접근 자체가 큰 문제, 큰 영향을 끼치지 않을 정도 규모의 자료구조라면 논외로 치더라도, 뷰 타입이 조금만 늘어나도 onCreateViewHolder, onBindViewHolder 의 덩치는 엄청 커질 겁니다.예를들면 맨 마지막 아이템 타입이 B 이고 현재 추가 될 아이템 타입이 A인 경우에는 다른 형태의 디바이더를 넣어야 한다던지 하는 추가적인 확장이 이루어져야 한다면 골치가 꽤 아플겁니다. 특히 저는 위 예와 비슷하게 뷰 타입에 따라 각기 다른 아래 위 마진값을 요구받을 때, ViewHolder 마다 이전 데이터를 참고하게 만들고 동적으로 Visibility 처리를 하거나 MarginLayoutParams 를 고치는 것이 비효율적으로 느껴져서 height를 주입받는 DividerViewHolder 를 하나 만들어 사용하곤 했습니다. 이렇게 하니 각각의 ViewHolder 들이 데이터들에 의존적이지 않게 코딩이 가능했었습니다. 한 가지 더 예를들어 리스트 중간 중간 광고가 보여지게 되고 이 광고 클래스는 완전히 다른 객체로부터 보여줘야 한다 라고 했을 때 MultiItemAdapter 를 이용하면 쉽게 해결이 가능합니다.정작 근 1년간 “잔디”를 만들면서는 자주 쓰진 않았는데, 작년부터 각광받기 시작한 MVP 패턴을 사용할 때 View 에서의 로직을 최소화 하려고 한다면 써먹을 수 있는 모델로 적합하지 않나 생각이 들면서 다시 사용하기 시작했습니다. Presenter 에서 Row 를 만들어 던져주면 View 는 그것을 그대로 사용하게 만들 수 있다는 생각이 들었거든요.(아직까지는 비교적 크지 않은 부분에서만 사용하게 되서 View(MainThread)에서 Row 를 만들게 코딩해 놓은 컴퍼넌트가 더 많네요 흑흑) 더 복잡한 구조를 갖는 컴퍼넌트를 만들어야 할 때는 비동기 스레드에서 Row 까지 만들어 내보내는 것도 해볼까 하는 생각도 듭니다.제 눈에만 괜찮은 구조인지, 생각지도 못한 치명적인 단점이 있진 않은지, 구조나 설계 측면에서 안 좋은 점은 있지 않은지, 논리없이 Generic 으로 “퉁” 치고 있는 코드는 아닌지, 여러가지가 많이 궁금합니다 ^^ MultiItemAdapter 를 쓴 것과 안 쓴것의 정말 심플한 비교 소스를 열어놓았습니다 MultiItemAdapter 또, 여러분들은 어떻게 구현하고 계신지요? 여러분의 관심이 필요합니다 ! :)#토스랩 #잔디 #JANDI #개발 #개발자 #인사이트 #경험공유
조회수 1514

비트윈이 사용자를 분석하는 방법

빅데이터분석이 최근 이슈가 되면서 관심이 많으실 것 같습니다. 비트윈팀도 데이터 분석 참 좋아하는데요, 저희도 한번 해보았습니다. 이번 포스팅에서는 비트윈팀의 데이터 분석 노하우를 아낌없이 공유해드립니다.왜 사용자의 데이터를 분석해야하는가요?비트윈같은 서비스는 초기 단계에는 앱을 기획하고 만들어낸 팀에 아이디어에 의해 계속해서 발전하고, 유지됩니다. 하지만 기능이 점점 다양해지고 사용자가 점점 많아지면서 사용자들의 앱 사용패턴을 점점 예측하기 어려워집니다. 게다가 비트윈은 해외 진출을 구상 중이었는데, 개인 혹은 팀의 아이디어만으로 해외에서의 사용패턴을 정확히 알기는 어려웠습니다.이런 시점에 필요한 것이 사용자 분석입니다.사용자들의 사용패턴을 분석해 보는 방법은 여러 가지가 있습니다. 초기에 해볼 수 있는 가장 직관적이고 쉬운 것은 비트윈을 사용하는 자기 자신의 사용 패턴을 돌아보고 분석해보는 것입니다. 또 친구들이나 익명 사용자들의 사용패턴을 물어보거나, 관찰하는 방법들이 있습니다. 이런 방법은 매우 효과적이고 많은 아이디어를 주지만 여러 가지 한계점이 있습니다. 지역적, 시간적인 한계 등이 그것입니다.그래서 택할 수 있는 방법이 실제로 사용자들의 행동을 컴퓨터로 수집해서 분석하는 것입니다. 말 그대로 '데이터 분석'을 하게 되는 것입니다.무엇을 분석할지 알아야 합니다데이터로 분석할 수 있는 것은 무궁무진합니다만, 먼저 데이터가 있어야합니다. 비트윈과 같이 서버와 통신하는 앱은 사용자들이 서버에 요청을 할 때마다 엑세스 로그를 남기게 됩니다. 이 엑세스 로그는 사용자들의 사용패턴을 고스란히 담고 있어, 소중한 데이터가 됩니다.엑세스 로그 분석은 전혀 어렵지 않습니다. 엑세스 로그에서 특정 행동에 해당하는 내용을 세는 것만으로도 여러 가지 유의미한 값을 얻어낼 수 있습니다. 하루 동안의 로그를 한줄씩 읽어서 메시지에 관련된 로그를 카운트하면 그날의 메시지 전송 건수를 얻을 수 있는 것입니다. (참 쉽죠?)엑세스로그에서 가입, 메시지, 사진, 메모 등 기본적인 내용에 해당하는 것들을 카운트하는 것만으로도 꽤 자세하게 앱 전체 사용자들의 전반적인 사용통계를 얻어낼 수 있습니다. 이제 해당 데이터를 엑셀에 넣어서 차트를 그려보면, 사용 통계에 대한 그럴싸한 차트가 그려집니다.엑세스 로그 분석에 성공했다면 좀 더 다양한 분석을 해볼 수 있을 텐데요, 사용자별 행동패턴 분석이나, 나라별, 혹은 아이폰, 안드로이드 디바이스별 분석 등 다양한 분석을 시도해볼 수 있습니다. 분석을 하기 전에 중요한 것은 무엇이 궁금한지, 어떻게 궁금한 데이터를 모을지 아이디어를 먼저 내는 것입니다. 여러 예제들을 찾아보며 공부해보면, 금방 좋은 아이디어를 얻으실 수 있을 겁니다.물론 여기서 중요한것은 개인정보나 사생활의 보호입니다. 로그가 유출되었을때의 보안 문제 뿐 아니라, 데이터 분석팀에게조차 개인정보가 노출된다면 곤란합니다. 이 문제에 저희가 어떻게 대처하고 있는지는 글 뒷부분에 자세히 알려드리겠습니다.특정 기술에 구애받지 말고 다양하게 구현해봅시다처음에는 로그 파일을 돌며 간단한 string을 검사하는 스크립트와 엑셀로도 충분했지만, 점점 복잡한 분석을 할수록 다양한 기술이 필요해집니다. 비트윈 사용자 분석도 점점 다양해지고 복잡해지면서 여러 가지 기술들을 사용하고 있습니다.비트윈 사용자 분석은 처음에는 6줄짜리 간단한 shell script에서 시작되었습니다.cat 2011-10-31.log | grep /messages | grep POST | wc -lcat 2011-10-31.log | grep /photos | grep POST | wc -lcat 2011-10-31.log | grep /memos | grep POST | wc -lcat 2011-10-31.log | grep /like | grep POST | wc -lcat 2011-10-31.log | grep SIGN | wc -lcat 2011-10-31.log | grep REL | grep POST | wc -l이런 스크립트를 만들어서 결과를 이메일로 공유하거나, 엑셀로 만들어 놓곤 했습니다.여기에 비트윈 분석은 조금 더 발전하여, 로그파일을 쿼리하여 Map Reduce 작업이 가능한 Hive를 사용하고, PHP로 통계 웹사이트를 만들어 차트를 그리기 시작했습니다. 이 방식은 처음에는 매우 편리했지만 차츰 쿼리만으로 원하는 결과를 얻기가 힘든 다소 복잡한 분석이 필요해지기 시작했습니다.현재는 모든 로그를 분산 데이터베이스인 HBase에 Date Key와 User Key로 넣고, 코드 생산성이 좋은 Scala로 직접 Map Reduce코드를 작성해서 데이터들을 분석하고 있습니다. 그래서 충분히 scalable하면서도 꽤 편리하게 이용할 수 있는 데이터베이스를 활용하고, Scala의 좋은 expression을 활용하여 짧고 유지보수나 확장이 쉬운 코드로 분석을 수행하면서도 Java와 호환되는 Scala의 특성을 이용하여 Map Reduce 코드 작성을 효과적으로 하고 있습니다. 이렇게 분석한 데이터는 MySQL에 넣어서 2차로 가공하고, Scala Web Framework인 Play Framework을 이용하여 분석 사이트를 구축하고 D3 Chart를 이용해서 Visualize하고 있습니다. 이렇게 함으로써 편리한 MySQL 쿼리 사용의 장점을 취하고 멋진 차트를 효과적으로 그려낼 수 있습니다.좋은 Visualization은 멋질 뿐만 아니라 손쉽게 아이디어를 공유할 수 있게 해줍니다.앞으로는 더 빠른 성능을 위해 Hive를 더 잘 사용해보거나, Elastic Search같은 index engine들을 사용해 볼 계획도 가지고 있습니다. 또한 End point들에서 직접 성능을 측정하여 중앙으로 모아서 분석해보려는 생각도 가지고 있습니다.기술을 선택함에 있어서 정답은 없는 거 같습니다. 널리쓰이는 MySQL같이 scalability가 좀 떨어지지만, 다양한 쿼리로 높은 생산성을 낼 수 있는 데이터베이스도 있고, HBase같이 scalability가 좋지만, 데이터를 저장하는 형태에 제한이 있어 생산성이 조금 떨어지는 데이터베이스도 있습니다. 저희는 앞서 소개드렸듯이 이 두 가지를 모두 혼용하여 사용하고 있습니다. 각자가 마주한 상황에 맞게, 또 각자가 익숙한 기술에 맞게 설계하고, 사용해보면 됩니다.개인정보 보호는 철저하게빅데이터 분석이 개인정보를 침해하는 빅 브라더가 될 수 있다는 우려들이 나오고 있습니다. 300만이 넘는 커플들의 비밀스러운 일기를 담고 있는 비트윈 서비스는 당연하게도 모든 업무를 진행하는 데 있어 보안과 개인정보를 최우선으로 하고 있습니다. 데이터 분석에서도 분석할 수 있는 내용을 상당히 제한받더라도, 예외 없이 그 원칙을 지키고 있습니다.비트윈의 API서버는 AWS클라우드에서 운영되고 있는데, 사용료가 상당히 비싸기 때문에 큰 컴퓨팅 파워를 사용해야 하는 데이터분석까지 AWS에서 하기엔 좀 부담이 되었습니다. 그래서 PC급 컴퓨터 여러 대를 구입하여 사무실 구석에 쌓아놓고 사용하고 있습니다.하지만 문제는 보안이었습니다. AWS의 비트윈 API서버는 다중으로 보안이 유지되고 있지만, 사무실에 있는 서버에 사용자들의 개인정보를 담아둘 수는 없는 일이었습니다. SECO*이 사무실을 지켜주고 있긴 하지만 보안회사에 고객들의 소중한 개인정보를 맡기고 안심할 수는 없으니까요. 그리고 설사 보안 문제가 잘 해결된다고 해도, 분석을 수행하는 비트윈 데이터분석팀원에 개인정보 혹은 사생활이 노출된다면 그 또한 문제라고 생각하였습니다.그래서 저희가 생각해낸 방법은 '익명화'입니다. Access Log들을 저장할 때 사용자의 아이디를 전부 단방향 salted-hash하여 누구인지 알 수 없게 만들었습니다. (물론 salt key는 데이터 분석팀은 알 수 없습니다.) 그리고 애초에 Access Log에는 '어떤 사람'이 '50글자짜리 메시지를 보냈다' 라던가, '사진을 올렸다' 정도만 기록이 되기 때문에, 이를 통계적으로 분석하는 것은 유의미하지만, 사적인 정보를 담고 있지는 않습니다.익명화되어 처리되고 있는 로그는 개인정보는 거의 담고 있지 않으면서도, 유익한 분석 결과를 만들어줍니다.이런식으로 운영을 한다면 데이터 분석팀에서도 사적인 정보(예: 메시지 내용)에 대해서는 접근할 수 없기 때문에, 회원들의 소중한 개인정보와 사생활을 지킬 수 있습니다. 어떤 분석을 수행할 때 언제나 비트윈팀은 언제나 보안과 사생활 보호의 원칙을 지킬 수 있는 범위에서만 진행하고 있습니다.아이디어의 공유, 그리고 액션아이템이 무엇보다도 중요합니다데이터 분석의 목표가 무엇인지, 왜 해야 하는지 생각해보면, 무엇을 해야 하는지 알 수 있습니다. 바로 분석으로부터 얻은 아이디어를 공유하고 액션아이템을 정하고 실천하는 것입니다.데이터를 visualization하는것이 중요한 이유가 여기에 있습니다. 보기 좋은 떡이 먹기도 좋다는 말이 있듯이, 데이터도 먹기 좋아야 합니다. 여러 사람이 쉽게 이해할 수 있어야 아이디어를 공유하고 의사결정을 내리기가 수월하기 때문입니다.민트&베리 사용량 분석. 연인들이 쓰는 앱이라 사랑표현이 인기가 많군요. 디자인팀이 이런 자료를 참고하여 이후 디자인 아이디어를 내는 데 도움이 되면 좋겠죠?비트윈팀은 매번 데이터 분석 미팅을 진행하고 나면 액션아이템을 정하고 실천합니다. 저희가 어떤 식으로 의사결정을 내리고 행동하는지에 대해서는 비트윈 팀블로그의 VCNC는 데이터분석에 기반해 어떤 결정을 내렸나 포스팅을 보시면 도움이 되실 것 같네요.맺으며이번 포스팅에서는 비트윈팀이 어떻게 무엇을 분석하는지 간단하게 다뤄봤습니다. 의견이나 참견 모두 환영이니 댓글 많이 남겨주세요! 다음번 포스팅엔 기술적인 부분에 대해 좀 더 자세하게 다뤄보도록 하겠습니다.저희는 언제나 타다 및 비트윈 서비스를 함께 만들며 기술적인 문제를 함께 풀어나갈 능력있는 개발자를 모시고 있습니다. 언제든 부담없이 [email protected]로 이메일을 주시기 바랍니다!
조회수 4100

리디북스 서비스 장애 복구 후기

지난 8월 26일에는 약 21분간 리디북스 서비스 전체가 중단되는 장애가 있었습니다.사실 서버 스택 일부에만 영향을 주는 장애는 눈에 잘 띄지 않지만 꽤 흔하게 발생하는 일입니다. 기기 1대당 외부적인 요인으로 인한 장애가 평균 2년에 1번 발생한다고 가정하면, 서버가 100대 있을 때는 대략 1주일에 1번꼴로 장애가 발생하는 셈입니다.이런 형태의 장애는 서버 스택의 한 곳에서만 발생하므로, 이중화 혹은 클러스터링을 통해서 극복하곤 합니다. 또한 원인이 명확하므로 해당 기술에 대한 이해도가 높다면 비교적 빠른 시간 내에 복구가 가능합니다.그러나 이번에 리디북스가 경험한 장애는 달랐습니다. 현재 리디북스는 2개의 데이터센터와 클라우드에 인프라가 분산되어 있는데, 이 중에서 1차 데이터센터의 전원 공급에 문제가 생겨 특정 서버 랙에 있는 서버 17대가 동시에 내려간 것입니다. 즉, 소프트웨어나 머신의 물리적인 장애가 아닌, 데이터센터의 장애였습니다. AWS로 비유를 하자면 가용 영역(Availability Zone)의 장애라고 할 수 있겠습니다.원인에 대해이번 장애의 근본적인 원인은 데이터센터가 전원을 정상적으로 공급해주지 못한 것입니다. 물론 데이터센터 혹은 클라우드 서비스(IaaS)는 고객사에게 전원과 네트워크를 안정적으로 제공해주어야 하는 의무가 있습니다.하지만 이들 역시 천재지변이나 사람의 실수에 대한 대비가 100% 완벽할 수는 없습니다. 따라서 이러한 점을 사전에 고려하고 인프라를 설계하지 못한 것이 2차적인 원인입니다.이번 계기를 통해 데이터센터 이중화를 계획하게 되었고, 사용 중인 클라우드 역시 지역(Region) 전체에 장애가 생길 경우에 대한 대비가 되어있지 않아, 이번 계기로 복제 계획(Geo-Replication)을 세우게 되었습니다.구체적인 상황당시 전원이 차단되어 강제 종료된 서버들은 아래와 같습니다.데이터베이스 프록시 x 2메인 리버스 프록시 x 1읽기 분산용 MySQL 슬레이브 x 1서점용 웹 서버 x 3추천 알고리즘 API 서버 x 1알림센터 API 서버 x 2메인 스토리지 서버 x 2출판 플랫폼용 데이터베이스 x 2테스트 및 배치 작업용 서버 x 3그림으로 표현해 보자면, 대략 아래와 같은 상황에서… 아래와 같은 상황이 된 셈입니다.서버 스택의 여러곳에 순간적으로 장애가 발생한 상황공인 IP가 할당된 메인 프록시 서버 중 1대가 내려갔지만, 실제로는 아래와 같이 가상 IP로 구성을 한 상태였기 때문에 대기 중인(stand-by) 프록시가 동작하여 곧 서점에 장애 공지를 띄울 수 있었습니다.[이미지 출처: DigitalOcean™]공지 이후의 움직임우리는 데이터센터의 복구 시점을 명확히 알 수 없어서 신규 구축(provisioning)을 시작함과 동시에, 서버들의 물리적인 위치 이동을 고려하고 있었습니다. 그러나 다행히 10분이 지난 시점에서 전원 문제는 해결되었고, 서버들은 순차적으로 부팅이 완료되었습니다.일부 서버들은 부팅 과정에서 예상치 못한 지연이 발생하기도 하였지만, 모든 서버의 부팅이 완료된 이후에도 서비스는 완전히 정상으로 돌아오지 않았습니다. 당시 우리가 겪었던 문제와 해결책은 아래와 같습니다.A. 읽기 분산용 MariaDB 슬레이브의 복제 지연(replication lag) 문제슬레이브 서버의 부팅이 완료되자 데이터베이스 프록시(HAProxy)는 해당 서버를 정상으로 간주하여 라우팅 대상에 포함하게 되었고, 애플리케이션 서버들은 정상적으로 커넥션을 맺기 시작하였습니다. 하지만 해당 슬레이브는 수십 분간 마스터를 따라잡지 못한 상태였기 때문에 최신 데이터가 보여지지 않는 문제(stale data)가 있었습니다. 우리는 즉시 해당 슬레이브를 제거하였고 지연이 사라진 이후에 다시 서비스에 투입하였습니다.B. 읽기 분산용 슬레이브의 웜업(warm-up) 문제복제 지연은 사라졌지만 서버의 CPU 사용량이 크게 높은 상태가 한동안 유지되었고, 응답속도는 정상적인 슬레이브에 비해서 많이 느렸습니다. 왜냐하면 캐시가 비워진 상태에서 바로 서비스에 투입되어, 캐시 미스가 휘몰아치는 현상(cache stampede)이 발생하였기 때문입니다. 따라서 간단한 쿼리도 평소보다 오래 걸렸고, 그대로 둔다면 커넥션풀이 꽉 차는 현상이 발생할 것으로 예상되었습니다.곧 우리는 HAProxy로 해당 서버의 가중치를 10%로 낮추어 인입되는 쿼리의 양을 조절하였으며 응답속도는 정상 수치로 돌아오게 되었습니다. 이후 스크립트를 작성하여 수동으로 캐시를 채워나감과 동시에 점차 가중치를 높여 처리량을 정상화하였습니다.프로덕션에서 사용하는 서버는 innodb_buffer_pool 이 100G 이상으로 매우 크게 설정되어 있으며, 재시작 시 캐시가 날아가는 현상을 해결하기 위해 innodb_blocking_buffer_pool_restore 옵션을 적용하고 있습니다. 하지만 지금처럼 메모리를 덤프하지 못하고 비정상 종료가 된 상황에서는 해당되지 않았습니다.C. 인메모리 데이터의 보존 문제알림센터는 다양한 프로모션과 개인화된 정보를 전달해주는 공간입니다. 알림센터의 특징은 데이터의 영구 보존(persistency)이 필요하지 않고, 매일 수백만 건의 개인화된 메시지가 기록된다는 것입니다. 이러한 특징은 인-메모리 데이터베이스에 적합하므로 우리는 Redis를 마스터/슬레이브로 구성하여 저장소로 사용하고 있었습니다.어떠한 이유로든 Redis를 재시작해야 할 경우가 생기면, 메모리 상의 데이터가 날아가는 것을 방지하기 위해 주기적으로 스냅샷을 남기고 있습니다만, 이번에는 로그가 마지막까지 기록되지 못한 상태에서 메모리의 데이터가 날아가 버렸습니다.다행히 알림 발송과 관련된 메타정보는 모두 MariaDB에 기록하고 있으므로, 우리는 이를 기반으로 소실된 시점부터의 알림을 순차적으로 재발송할 수 있었습니다. 물론 모든 알림이 신규 상태로 간주되어 아이콘이 잘못 노출되는 문제가 있었지만, 고객님들은 너그럽게 이해해 주신 것 같습니다. 😅그래서 앞으로는?리디북스 DevOps 멤버들은 이번 데이터센터 장애를 통해 현재 인프라의 한계점을 실감하였고, 앞으로의 개선 방향에 대해 고민하게 되었습니다.몇 가지를 정리하면 다음과 같습니다.랙 단위로 장애가 발생할 수 있음을 인지하고 대비하자.같은 기능을 하는 서버를 하나의 랙이나 같은 가용 영역에 두지 말자.2차 데이터센터는 더 이상 옵션이 아닌 필수다.낙뢰나 지진으로 인해 데이터센터에 문제가 생길 수도 있다.긴급하게 프로비저닝이 필요한 상황에 대비하자.문서화가 되어 있더라도 경험이 없다면 동일한 구성에 많은 시간이 소요된다.모든 구성요소들에 대한 Ansible 스크립트를 작성하여두자.캐시 웜업 스크립트도 작성하여 두자.백엔드 구성요소들 간의 불필요한 의존 관계를 끊자.단 한 줄의 코드라도 참조하고 있다면 이는 독립적인 것이 아니다.언제나 서비스 지향적인 설계를 추구하자.Uptime을 관리하자.최대 180일을 기점으로 무조건 리부팅을 하자.재시작 과정에서 다양한 문제와 개선점이 발견될 것이다.커널 패치, 보안 패치를 할 수 있는 것은 덤이다.아래와 같은 긍정적인 면도 발견하였습니다.장애 상황이 실시간으로 Slack 채널을 통해 전파되었음진행 상황에 대해 모두가 동일한 수준으로 이해할 수 있었다.모니터링 연동(integration) 기능 때문에라도, Slack은 유료로 구매할만한 값어치가 충분하다.같은 기능을 하는 서버들이 다른 랙에 많이 분산되어 있었다.인프라가 확장될 때마다 빈 공간에 필요한 서버를 추가했을 뿐이지만, 자연스럽게 물리적인 위치가 분산되는 효과가 있었다.이 외에도 특정 클러스터를 구성하는 노드들을 분산하여 배치시키자.서버별로 오너쉽이 부여되어 있어서 빠르게 복구가 된 점여러 명의 백엔드 개발자들이 병렬적으로 복구를 진행할 수 있었다.마지막으로넷플릭스의 엔지니어들은 무질서한 원숭이(Chaos Monkey)라는 프로그램을 만들어서 운영한다고 합니다. 이 원숭이는 서비스 인스턴스들을 무작위로 중단시키는 역할을 합니다. 다소 황당하게 들리지만, 넷플릭스에는 일부 서비스에 장애가 발생하더라도 나머지 부분은 문제없이 운영되어야 한다는 원칙이 있으므로, 이를 수시로 시뮬레이션하는 과정을 통해 복구 능력을 높여둔다는 것입니다.실제로 이렇게 급진적인 아이디어를 실천할 수 있는 회사는 매우 드물 것입니다. 하지만, 우리는 이번 계기를 통해 무질서한 원숭이의 필요성을 절감하였고, 이로 인해 서버를 주기적으로 리셋하는 정책을 만들게 되었으며 모든 단일 장애점(SPoF)에 대한 대비를 시작하게 되었습니다.장애를 단순히 피해라고만 생각한다면, 서로를 비난하고 책임을 전가하는 상황이 펼쳐질 것입니다. 하지만 고객의 불편함과 맞바꾼 매우 비싼 경험이라고 생각한다면, 보다 튼튼하고 회복탄력적인 시스템을 갖추기 위해 노력하게 될 것입니다. 그러다 보면 언젠가는 데이터센터 전체에 문제가 생겨도 버틸 수 있는 모습으로 진화할 것이라고 생각합니다.#리디북스 #장애복구 #역경돌파 #개발 #개발후기 #개발자 #서버개발 #서버
조회수 1720

PyCon2017 첫번째날 후기

아침에 느지막이 일어났다. 어제 회사일로 피곤하기도 했지만 왠지 컨디션이 좋은 상태로 발표를 하러 가야지!라는 생각 때문에 깼던 잠을 다시 청했던것 같다. 일어나 아침식사를 하고 아이 둘과 와이프를 두고 집을 나섰다. 작년 파이콘에는 참가해서 티셔츠만 받고 아이들과 함께 그 옆에 있는 유아교육전을 갔었기에 이번에는 한참 전부터 와이프에게 양해를 구해둔 터였다.코엑스에 도착해서 파이콘 행사장으로 가까이 가면 갈수록 백팩을 메고, 면바지를 입고, 영어 글자가 쓰인 티셔츠를 입은 사람의 비율이 높아지는 것으로 보아 내가 제대로 찾아가고 있구나 라는 생각이 들었다.늦게 왔더니 한산하다.지난번에는 입구에서 에코백과 가방을 나눠줬던 것 같은데 이번에는 2층에서 나눠준다고 한다. 1층이 아무래도 복잡해지니 그런 것 같기도 하고, 2층에서 열리는 이벤트들에도 좀 더 관심을 가져줬으면 하는 것 같기도 하다. 우선 스피커 옷을 받고 싶어서 (솔직히 입고 다니고 싶어서) 2층에 있는 스피커방에 들어갔다.허락 받지 않고 사진찍기가 좀 그래서 옆방을 찍었다첫 번째 키노트는 놓쳤지만 두 번째 키노트는 꼭 듣고 싶었기에 간단히 인사만 하고 티셔츠를 들고 나왔다. (외국에서 오신 연사분과 영어로 대화를 나누고 있어서 자리를 피한것은 아니다.) 나가는 길에 보니 영코더(초등학교 5학년 부터 고등학생 까지 파이썬 교육을 하는 프로그램)을 진행하고 있었다. 의미있는 시도를 하고 있다는 생각이 들었다.이 친구들 2년 뒤에 나보다 잘할지도 모른다.키노트 발표장에 갔더니 아웃사이더님이 뒤에 서 게셨다. 지난 파이콘 때 뵙고 이번에 다시 뵈었으니 파이콘이 사람들을 이어주는 역할을 하는구나 싶었다.키노트에서는 현우 님의 노잼, 빅잼 발표 분석 이야기를 들을 수 있었다. 그리고 발표를 통해 괜히 이것저것 알려줘야만 할 것 같아 발표가 부담스러워지는 것 같다는 이야기를 들었다. 나 또한 뭔가 하나라도 지식을 전달해야 한다는 압박감을 느끼고 있었던 터라 현우 님의 키노트 발표를 듣고 나니 좀 더 오늘을 즐겨야겠다는 생각이 들었다.오늘은 재미있었습니다!현우님 키노트를 듣고 같은 시간(1시)에 발표를 하시는 경업님과 이한님 그리고 내일 발표이신 대명님, 파이콘 준비위원회를 하고 계신 연태님과 함께 식사를 하러 갔다. 가는 길에 두숟갈 스터디를 함께 하고 계신 현주님과 희진 님도 함께했다. 사실 이번에는 발표자도 티켓을 사야 한다고 해서 조금 삐져 있었는데 양일 점심 쿠폰을 주신다고 해서 삐진 마음이 눈 녹듯이 사라졌다.부담 부담식사를 하고 발표를 할 101방으로 들어가 봤다. 아직 아무도 없는 방이라 그런지 괜히 긴장감이 더 생기는 느낌이다. 발표 자료를 열어 처음부터 끝까지를 한번 넘겨 보고 다시 닫았다. 처음에는 가장 첫 발표라 불만이었는데 생각해보니 발표를 빨리 마치고 즐기는 게 훨씬 좋겠다는 생각이 들었다. 발표 자료를 다듬을까 하다가 집중이 되지 않아 밖으로 나갔다. “열린 공간” 현황판에 충동적으로 포스트잇을 하나 붙이고 왔다. 어차피 발표는 나중에 온라인으로도 볼 수 있으니까 사람들과 이야기를 나눠 봐야 겠다 싶었다. (내 발표에는 사람이 많이 왔으면 하면서도, 다른 사람의 발표는 온라인으로 보겠다는 이기적인 생각이라니..)진짜 궁금하긴 합니다다시 발표장으로 돌아왔다. 왠지 모르는 분들은 괜찮은데 아는 분들이 발표장에 와 계시니 괜히 더 불안하다. 다른 분들은 발표자료에 짤방도 많이 넣으셨던데.. 나는 짤방도 없는 노잼 발표인데.. 어찌해야 하나. 하지만 시간은 다가오고 발표를 시작했다.얼굴이 반짝 반짝리허설을 할 때 22분 정도 시간이 걸렸던 터라 조금 당겨서 진행을 했더니 발표를 거의 20분에 맞춰서 끝냈다. 그 뒤에 몇몇 분이 오셔서 질문을 해주셨다. 어리버리 대답을 한 것 같다. 여하튼 내 발표를 찾아오신 분들께 도움이 되었기를. 그리고 앞으로 좀 더 정확한 계산을 하시기를.대단히 발표 준비를 많이 하지도 못하면서 마음에 부담만 쌓아두고 있는 상황이었는데, 발표가 끝나니 아주 홀가분한 마음이 되었다. 발표장을 나가서 이제 부스를 돌아보기 시작했다. 매해 참여해 주고 계신 스마트스터디도 보이고 (정말 안 받고 싶은 ‘기술부채’도 받고 말았다.) 쿠팡, 레진 등 친숙한 회사들이 많이 보였다. 내년에는 우리 회사도 돈을 많이 벌어 여기에 부스를 내고 재미있는 이벤트를 하면 좋겠다는 생각이 들었다.부스를 돌아다니다가 이제 파이콘의 명물이 된 내 이름 찾기를 시작했다. 이름을 찾기가 쉽지가 않다. 매년 참여자가 늘어나서 올해는 거의 2000명에 다다른다고 하니 파이썬 커뮤니티의 성장이 놀랍다. 10년 전에 파이썬을 쓸 때에는 그리고 첫 번째 한국 파이콘이 열릴 때만 해도 꽤 마이너 한 느낌이었는데, 이제 주류가 된 것 같아 내 마음이 다 뿌듯하다. (그리고 내 밥줄이 이어질 수 있는 것 같아 역시 기쁘다)어디 한번 찾아보시라다음으로는 박영우님의 "Django admin site를 커스텀하여 적극적으로 활용하기” 발표를 들으러 갔다. (짧은 발표를 좋아한다.) 알고 있었던 것도 있었지만 커스텀이 가능한지 몰랐던 것들도 있어서 몇 개의 기능들을 킵해 두었다. 역시 컨퍼런스에 오면 내게 필요한 ‘새로운 것’에 대한 실마리를 주워가는 재미가 있다.익숙하다고 생각했지만 모르는것이 많다4시가 되어 OST(Open Space Talk)를 하기로 한 208B 방으로 조금 일찍 갔다. 주제가 뭐였는지는 잘 모르겠는데 주식 투자, Tensor Flow, 비트코인, 머신러닝 등등의 이야기들이 오가고 있었다. 4시가 되어 내가 정한 주제에 대해 관심 있는 사람들이 모였다. 괜히 모일 사람도 없는데 큰방을 잡은 것이 아닐까 하고 생각하고 있었는데, 생각보다 많은 분들이 오셨다.각 회사들이 어떤 도구를 사용하는지 설문조사도 해보고, 또 어떤 개발 방법론을 사용하는지, 코드 리뷰, QA는 어떻게 하고 있는지에 대한 이야기를 나눴다. 다양한 회사에서 다양한 일을 하는 사람들이 모여 있다 보니 생각보다 꽤 재미있게 논의가 진행되었다. 사실 내가 뭔가 말을 많이 해야 할 줄 알았는데, 이야기하고 싶은 분들이 많이 있어서 진행을 하는 역할만 하면 되었다. 마지막으로는 “우리 회사에서 잘 사용하고 있어서 다른 회사에도 추천해 주고 싶은 것”을 주제로 몇 가지 추천을 받은 것도 재미가 있었다.열심히 오간 대화를 적어두긴 했다5시에 OST를 마치고는 바로 집으로 돌아왔다. 오늘 저녁에 아이들을 잘 돌보고 집 청소도 열심히 해두어야 내일 파이콘에 참여할 수 있기 때문이다. 기대된다. 내일의 파이콘도.그리고 정말 감사드린다. 파이콘을 준비해주시고 운영해주고 계신 많은 분들께.#8퍼센트 #에잇퍼센트 #개발자 #개발 #파이썬 #Python #파이콘 #Pycon #이벤트참여 #참여후기 #후기
조회수 5386

안드로이드 백그라운드 서비스 개발시 고려해야 할 사항

지난 시간엔 사운들리 백엔드에 대해 설명을 드렸었죠. 이번 시간엔 사운들리 서비스중 클라이언트에 해당하는 안드로이드 SDK, 그 중에서도 백그라운드 서비스에 초점을 맞추어 설명을 해 볼까 합니다.안드로이드의 특징 중 하나로 Service 를 들 수 있습니다. 이 서비스란 녀석은 백그라운드에서 실행 될 수 있다는 점이 가장 큰 특징인데요. 물론 iOS 에서도 일부 지원은 합니다만 매우 제한적인 경우(음악 재생 등)에만 사용 가능합니다.제가 생각하는 백그라운드 서비스 개발 시 유의 사항은 아래와 같습니다.동작 기간 - 상시 동작 해야 하는가, 특정 조건에서 특정 작업을 할때만 동작 해야 하는가글로벌 프로세스 사용 유무 - 서로 다른 어플리케이션에서 접근이 가능 해야 하는가동작 조건 - 특정 시간 혹은 기간마다 동작 해야 하는가, 특정 이벤트 발생시 동작 해야 하는가그 외에도 많은 부분들이 있지만 일단 저 정도만 고려해도 개인적인 생각으로는 충분히 개발 가능하다고 생각 합니다.그러면 각각에 대해서 좀 더 자세하게 알아 볼까요?1. 동작 기간동작 기간에 대해서 이야기 하기 전에 먼저 유저 레벨에서 가장 많이 사용하는 Service 와 IntentService 의 차이점에 대하여 짚고 넘어가보겠습니다.Service 를 상속`Context#startService//Context#stopService` 혹은 `Context#bindService(w/BIND_AUTO_CREATE)//Context#unbindService` 를 통해 수명 조절 (Service 내에서 Service#stopSelf 를 호출하는 방법도 있습니다.)Service 시작된 이후에 커뮤니케이션 가능수명 종료 API(stopService or unbindService) 를 호출 하기 전에는 프로세스가 사라지지 않음 (물론 LMK에 의해서 종료 된다던지 등등이 있지만 여기서는 논외로 하겠습니다.)IntentService 를 상속startService 를 통해 서비스를 시작함사용자가 따로 수명 관리를 할 필요가 없음상기 특징을 보면 Service 는 상시 동작하는 서비스에, IntentService 는 특정 조건에서 동작하는 서비스에 더 특화된 것을 볼 수 있습니다.사운들리 서비스는 백그라운드에서 상시 신호를 감지해야 하므로 Service 를 상속해서 쓰고 있습니다.2. 글로벌 프로세스 사용 유무안드로이드 컴포넌트 속성 중 android:process  속성을 소문자로 시작하는 이름을 쓰면 글로벌 프로세스로 사용 할 수 있습니다. 글로벌 프로세스니까 당연히 다른 어플리케이션에서도 접근이 가능하답니다.아래와 같은 경우에는 글로벌 프로세스를 사용 할 때 더 이점이 있습니다.불필요한 리소스 사용 자제 - 서버와 통신하는 모듈의 경우, 여러 앱에서 동일한 모듈이 사용 될 때 하나의 통로만 사용 하는 것이 네트워크 리소스를 적게 먹습니다.공유 불가능한 자원 사용 - 사운들리 SDK 가 이 경우에 해당합니다. 비가청 대역 음파를 사용하는 특성상 마이크를 사용 해야 하나 안드로이드에서는 서로 다른 어플리케이션 간의 마이크 공유가 불가능합니다.하지만 일반적인 어플리케이션 서비스는 굳이 글로벌 프로세스를 쓸 필요가 없습니다. 모듈 버전에 따른 실행, 데이터 공유 등 골치 아픈게 이것저것이 아니에요... ㅠ3. 동작 조건동작 조건은 크게 time base 와 event base 로 나눌 수 있는데요. 각각의 경우에 서비스를 동작 시킬 트리거를 다르게 쓰는 것이 좋습니다.동작 조건에 따라 안드로이드에서 사용 가능한 트리거는 아래와 같습니다.시간 기반 (time base)AlarmManager 의 alarm API (set, setExact, setExactAndAllowWhileIdle 등)Android System Broadcast (ACTION_TIME_TICK 등)GCM Message이벤트 기반 (event base)Android System Broadcast (ACTION_SCREEN_ON, ACTION_POWER_CONNECTED 등)GCM Message그 외 각종 어플리케이션 사용시 발생되는 이벤트위에서 이야기한 것을 표로 정리하면 아래와 같습니다.동작 기간동작 조건사용해야할 서비스동작 트리거그 외상시동작시간기반 동작Service 를 상속 받아 startService 서비스 시작bindService 를 통해 서비스와 연결하여 커뮤니케이션해당 Service 는 START_STICKY 로 실행AlarmManager 혹은 서버에서 주기적으로 동작하는 GCM Message 사용글로벌 프로세스를 사용 해야 한다면 android:process 속성을 사용이벤트 기반 동작System Broadcast 혹은 GCM Message, JobService 등을 사용작업 할때만 동작시간 기반 동작IntentService 를 상속받아 startservice 로 실행Intent 에 작업 관련된 파라매터를 전달AlarmManager 혹은 서버에서 주기적으로 동작하는 GCM Message 사용이벤트 기반 동작System Broadcast 혹은 GCM Message, JobService 등을 사용Etc. 유의해야 할 부분추가로 백그라운드 서비스 개발 시 유의해야 할 점들을 기술 해 보겠습니다.i) 배터리 절전 기술안드로이드 버전이 올라갈수록, 그리고 벤더들의 기술력이 높아질수록 배터리 절전 기술 역시 발전합니다. 이러한 기술들은 사용자 입장에서는 반가운 기술이지만 개발자들에게는 종종 절망을 선사합니다 ㅜㅜ사운들리 서비스도 개발 과정에서 각종 절전 기술 때문에 고생을 했는데요, 크게 고생한 기술 및 특징은 아래와 같습니다.DozeAndroid 6.0 이후 버전에 적용아래의 상태에서 일정 시간 이후 Doze 모드 진입충전 중이 아님스크린 꺼져 있음일정 수치 이상 움직이지 않음제한되는 사항AlarmManager 의 AlarmJobServiceWakeLock 무시네트워크 접근 제한회피 방법AlarmManager#setExactAndAllowWhileIdle() - Doze 에서도 동작하지만 최대 15분에 한 번씩만 동작 가능GCM high priority messageApp StandbyAndroid 6.0 이후 버전에 적용일정 기간 동안 아래 상황 중 하나도 발생하지 않은 경우 시스템에서 해당 앱을 standby state 로 간주명시적 앱 실행액티비티나 서비스가 포그라운드(전경)에서 실행 중, 혹은 포그라운드에서 실행 중인 앱이 해당 앱의 컴포넌트 사용중알림을 생성하고 유저가 잠금 화면이나 알림 트레이에서 확인한 경우제한되는 사항네트워크 사용 및 동기화 기능 사용 불가회피방법유저와 상호 작용유저가 디바이스 충전스마트 매니저삼성에서 킷캣 (안드로이드 4.4) 이후의 모델 (일부 제외)에 적용일정 시간 이상 유저가 사용하지 않은 앱은 알림 생성 불가관련글: 구글 개발자 블로그의 Android M 관련 변경점ii) LMK (Low Memory Killer)안드로이드의 각각의 프로세스는 특성에 따라 상태가 부여됨각 상태는 제한되는 메모리 사이즈가 정해져 있고, 디바이스의 가용 메모리가 해당 사이즈 이하로 떨어질 시 시스템에서 프로세스를 종료START_STICKY 로 실행한 서비스의 경우 일정 시간 이후에 null Intent 를 가진채로 재시작킷캣 이상에서 PID가 0이 된 채로 남아있는 버그가 있음ActivityManager#getRunningServices 에서 서비스 리스트를 가져 왔을때 찌꺼기가 존재마치며보기엔 복잡해 보이지만 사실 서비스 기획에 맞게 기능들을 골라서 쓰기만 하면 되니까 생각보단 복잡하진 않습니다. '사용자 중심의 나이스한 서비스 기획' 만 있으면 위의 표에서 기능을 골라서 조립만 하면 됩니다.물론 실제 개발 시에는 훨씬 더 고민 해야 될 부분이 많을 겁니다. 네트워크 트래픽도 최소화 해야 하고, WakeLock 도 적절히 써야 하고, 글로벌 프로세스 사용시는 DB 동기화도 시켜야 하고 GCM 은 downstream 이냐  group 이냐 topic 이냐 등등...개인적인 전망으론 장기적으로 Google 에서도 iOS 처럼 백그라운드 서비스 사용에 점점 제한을 둘 것 같습니다. 하지만 완전히 없애진 않을 것 같네요. 나름 특색 이니까요. 그러니 없애지만 않으면 방법을 찾아 낼 수 있을 겁니다.너무 두서없이 주저리주저리 쓴 글 같지만 조금이라도 도움이 되었으면 좋겠습니다.#사운들리 #개발 #개발자 #안드로이드 #안드로이드개발 #앱개발 #앱개발자 #SDK #인사이트 #조언 #경험공유
조회수 2299

시간을 줄여주는 CodeStar 사용 팁

편집자 주: 함께 보면 좋아요!애플리케이션 개발부터 배포까지, AWS CodeStarOverview: 작성 환경AWS CodeStar를 사용하면 애플리케이션의 서버, 언어 , 형상관리, 배포, 빌드까지 한꺼번에 관리할 수 있습니다. AWS를 사용하는 개발자라면 꼭 필요한 도구이기도 합니다. 이번 글에서는 CodeStar를 초기 설정할 때의 도움이 될 내용들을 소개하겠습니다.-서비스: AWS CodeStar-템플릿: Python Webservice, AWS Lambda목차파라미터 바인딩람다 환경변수 설정람다 레이어 설정xray 모니터링 설정람다 함수명 설정Global 섹션로컬 개발환경에서의 SAM 실행CodeStar 프로젝트 생성 후CodeStar로 프로젝트를 생성하면 소스코드와 배포를 위한 Code 시리즈 리소스들이 함께 만들어집니다. CodeCommit, CodeBuild, CodePipeline 등이 있습니다. 우선 기본으로 구축된 파이프라인부터 살펴보겠습니다.CodeCommit 리포지토리의 마스터 브랜치 코드를 변경하면 CodeBuild와 CloudFormaton 서비스를 통해 빌드, 테스트, 배포를 진행할 수 있게 설정되어 있습니다. 생성된 리포지토리의 template.yml 파일을 이용하면 프로젝트 리소스도 관리할 수 있는데, 특히 template.yml을 통해 CloudFormation으로 관리하는 리소스까지도 관리가 가능합니다.기본으로 생성된 template.yml 파일을 자세히 살펴보겠습니다.AWSTemplateFormatVersion: 2010-09-09 Transform: - AWS::Serverless-2016-10-31 - AWS::CodeStar Parameters: ProjectId: Type: String Description: CodeStar projectId used to associate new resources to team members CodeDeployRole: Type: String Description: IAM role to allow AWS CodeDeploy to manage deployment of AWS Lambda functions Stage: Type: String Description: The name for a project pipeline stage, such as Staging or Prod, for which resources are provisioned and deployed. Default: '' Globals: Function: AutoPublishAlias: live DeploymentPreference: Enabled: true Type: Canary10Percent5Minutes Role: !Ref CodeDeployRole Resources: HelloWorld: Type: AWS::Serverless::Function Properties: Handler: index.handler Runtime: python3.7 Role: Fn::GetAtt: - LambdaExecutionRole - Arn Events: GetEvent: Type: Api Properties: Path: / Method: get PostEvent: Type: Api Properties: Path: / Method: post LambdaExecutionRole: Description: Creating service role in IAM for AWS Lambda Type: AWS::IAM::Role Properties: RoleName: !Sub 'CodeStar-${ProjectId}-Execution${Stage}' AssumeRolePolicyDocument: Statement: - Effect: Allow Principal: Service: [lambda.amazonaws.com] Action: sts:AssumeRole Path: / ManagedPolicyArns: - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole PermissionsBoundary: !Sub 'arn:${AWS::Partition}:iam::${AWS::AccountId}:policy/CodeStar_${ProjectId}_PermissionsBoundary' 파라미터 바인딩Parameters 섹션에서는 ProjectId, CodeDeployRole, Stage 등 템플릿에서 사용할 파라미터를 지정할 수 있습니다. yml 파일 안에서는 ${ProjectId} 와 같이 사용할 수 있고, CodePipeline 환경에서 파라미터를 전달할 수 있습니다.CodePipeline → Deploy → GenerateChangeSet → Advanced → Parameter overrides람다 환경변수 설정람다 함수에서 사용할 환경변수를 설정할 수 있습니다. 아래와 같이 람다 환경변수 TZ(timezone)를 지정하면 실행 환경의 표준 시간대 설정이 가능합니다.Resources: HelloWorld: Type: AWS::Serverless::Function Properties: Environment: Variables: TZ: 'Asia/Seoul' 람다 레이어 설정람다 레이어를 적용하면 패키지 관리가 훨씬 편리해집니다. 함수의 패키지 크기가 3MB를 넘지 않으면 콘솔에서 코드를 직접 확인 및 수정할 수 있습니다. 람다 레이어는 zip 파일로 관리되고, /opt 폴더에 압축 해제되며 생성됩니다.람다는 250MB의 제한이 있습니다. 만약 레이어를 사용해 분리하더라도 람다함수패키지와 람다 레이어의 합으로 걸려있으므로 크기 제약에서 벗어날 수는 없습니다.Resources: HelloWorld: Type: AWS::Serverless::Function Properties: Layers: - arn:aws:lambda:{region}:{id}:layer:{layer-name}:{version} xray 모니터링 설정Tracing Property를 이용하면 람다 함수의 Enable active tracing 설정을 할 수 있습니다. CloudFormation 템플릿 메뉴얼엔 TracingConfig로 안내하고 있어도 빌드에 실패하여 확인해보니 SAM 템플릿의 AWS::Serverless::Function 의 스펙에선 Tracing으로 안내되고 있는 걸 볼 수 있었습니다.Resources: HelloWorld: Type: AWS::Serverless::Function Properties: Tracing: Active 람다 함수명 설정람다 함수는 기본적으로 아래와 같은 이름을 부여합니다.awscodestar-{brandi-test(프로젝트명)}-lambda-{HelloWorld(template함수ID)}-{NZ6YXLZ8XD0O(RANDOM_ID)}만약 함수 간의 호출이 필요할 때는 아래와 같이 함수 이름의 지정도 가능합니다.Resources: HelloWorld: Type: AWS::Serverless::Function Properties: FunctionName: !Sub '${ProjectId}-HelloWorld-${Stage}' Global 섹션Global 섹션을 이용하면 리소스마다 동일하게 적용할 항목들을 관리할 수 있습니다.Globals: Function: Runtime: python3.6 Environment: Variables: TZ: 'Asia/Seoul' VpcConfig: SubnetIds: - subnet-a1111111 - subnet-b2222222 SecurityGroupIds: - sg-c2222222 로컬 개발환경에서의 SAM 실행API Gateway 환경 실행sam local start-api 람다 함수 직접 실행echo ‘{}’ | sam local invoke —parameter-values=‘ParameterKey=ProjectId,ParameterValue=brandi-test’ HelloWorld Conclusion지금까지 CodeStar 초기 설정에 도움이 될 내용들을 살펴봤습니다. 강력한 기능들과 함께 업무를 진행한다면 조금이라도 더 나은 개발 환경을 구축할 수 있을 거라 생각합니다.글이상근 실장 | R&D DO실[email protected]브랜디, 오직 예쁜 옷만
조회수 1387

개발팀의 유행어 제조기, Mark를 만나다

 * 2015년에 작성된 글입니다편집자 주: 잔디에는 현재 40명 가까운 구성원들이 일본, 대만, 한국 오피스에서 일하고 있습니다. 국적, 학력, 경험이 모두 다른 멤버들. 이들이 어떤 스토리를 갖고 잔디에 합류했는지, 잔디에서 무슨 일을 하고 있는지 궁금해하시는 분들이 많았습니다.  이에 잔디 블로그에서는 매 주 1회 ‘맛있는 인터뷰’라는 인터뷰 시리즈로 기업용 사내 메신저 ‘잔디’를 만드는 사람들의 이야기를 다루고자 합니다. 인터뷰는 매 주 선정된 인터뷰어와 인터뷰이가 1시간 동안 점심을 함께 하며 다양한 이야기를 나누며 진행됩니다. 인터뷰이에 대해 궁금한 점은 댓글 혹은 이메일([email protected])을 통해 문의 부탁드립니다.인터뷰 시작에 앞서 편집자 스스로 잔디의 개발팀에 궁금한 점이 있었다. 매 주 수요일 아침 8시, 오피스 근처 카페에서 스터디를 하는 그들의 문화가 바로 그 것이다. 회사의 강요가 아닌 공부를 하겠다는 자발적인 이유로 모인다는 그들. 그들 중 한 명인 Mark를 이번 주 맛있는 인터뷰에 어렵게 모시게 되었다.세렝게티의 한 마리 표범과 같은 그의 눈빛이 향한 곳은 가.츠.나.베반갑습니다. 우리 좀 걷지 않았나요? 회사에서 꽤나 멀리 떨어진 ‘오무라안’을 온 특별한 이유가 있다면?회사 바로 앞에 있는 ‘탄’보다는 조금 고급스러운 일식 레스토랑이에요. 우연히 알게 된 곳인데 맛이 딱 제 취향이라 즐겨 찾습니다. 항상 가츠나베를 먹는데요. 그 맛은.. 말로 형용하기 어렵네요.가츠나베성애자이시군요. 얼마나 있는지 모르겠으나 ‘맛있는 인터뷰’ 독자들을 위해 인사 부탁드립니다.안녕하세요, 부산 남자 Mark입니다. 잔디에 합류한 지 약 두 달 정도 되었어요. 잔디에서는 Front-end 개발 업무를 맡고 있습니다.주로 어떤 일을 하시나요?쉽게 말하자면 사용자들이 접하는 부분을 책임지는 역할이에요. 지금은 Jihoon, Young과 함께 일하고 있는데 궁합이 잘 맞는 것 같아요. 사람이 적으면 할 수 있는 일이 한정되어 있고 반면 사람이 많으면 커뮤니케이션이 힘든데 저희 세 명은 예외인 것 같습니다.왔노라, 보았노라, 달렸노라Mark님만의 유행어가 있더라고요?‘가자!’ 를 말씀하시는 것 같은데요. 맞나요? (웃음) 비글로벌 서울 2015 우승 후, 뒷풀이 회식에서 흥에 겨워 술과 함께 외친 ‘가자!’가 다른 분들에게 인상적으로 각인되었던 것 같아요.네, 저도 그 자리에 있었는데요. 굉장히 인상적이었어요. 술이 센 편이신 것 같은데요?아니에요. 사실 술을 잘 하는 편도, 자주 마시는 편도 아니에요. 주량이라면 소주 두 병 정도? 그 날은 저희 회사가 좋은 일도 있고 해서 평소보다 많이 마시긴 했지만 기분이 좋았던 게 그런 사태를 만든 주된 이유인 듯 합니다.잔디 비글로벌 서울 2015 우승!잔디의 개발자 채용 과정이 다른 곳에 비해 까다롭다고 들었어요. 직접 경험하신 분으로서 어땠는지 여쭤볼 수 있을까요?정말 까다로워요. 다른 곳도 코딩시험을 보기는 하는데 잔디는 인사부에서 1차 코딩 시험을 보고 2차 면접에서는 왜 그렇게 코딩을 했는지 설명을 해야 합니다. 그리고 나서 인성 면접을 봤습니다. (잔디에서는 이 면접을 Behavior Interview 라고 부르며, 여러 부서의 인원들이 참여해 해당 인터뷰이가 함께 일할 사람으로서 적합한지 판단하게 된다 – 편집자 주)마치 수험생 같다는 느낌이 들었어요. 면접 과정 중에는 ‘뭐 이리 깐깐하게 굴어?’ 라는 생각을 했었는데, 지금 돌이켜 보면 이런 과정을 거쳐 합류한 인재들이 모여 있어 잔디가 빠르게 성장할 수 있지 않을까 추측해 봅니다.잔디에서의 생활은 어떤가요?신기한 점이 참 많은 것 같아요. 좋은 점은 출중한 능력을 가진 분들이 많다는 점이에요. 그분들을 통해 배울 점도 많고, 개인적으로는 분발해야겠다는 생각을 하게 해요. 많은 자극을 받고 있어요.신기한 점이라면 어떤 부분일까요?예를 들면 아침에 출근하면 Dan(CEO)이 제게 다가와 영어로 말을 건네는 것이 가장 신기한 것 같아요. 당황스러우면서도 한편으로는 신기해요.이건 개인적으로 궁금한 건데요. 개발팀의 아침 스터디에 대해 어떻게 생각하시나요?사실 아직 참여해 보진 못했어요. 잔디 개발팀에서는 매주 아침 8시까지 나와서 자발적으로 스터디를 하고 있는데요. 강요가 아닌 자발적으로 업무 외에 스터디를 한다는 점이 참 인상 깊어요.그렇군요. 질문을 좀 바꿔볼게요. 쉬는 날엔 뭐 하시나요? 부산 사람이니 야구?보통 쉬는 날엔 서울에 있는 친구들을 만나거나 게임을 해요. 야구는 부산 사람이다 보니.. 삶의 일부 같은 느낌이죠. 우리가 공기를 좋아하거나 싫어할 수 없듯, 야구 역시 좋아하거나 싫어할 수 있는 대상이 아니에요.보통 ‘부산 사람=야구’라고 생각하는데 Mark도 여기에 해당하는 분이었군요. 게임은 어떤 걸 즐겨 하시나요?WOW(Wolrd of Warcraft)라고 아세요? 저는 게임에 있어서 저만의 철학이 있어요. 게임에도 레벨이 존재한다고 생각하는데요. 모바일 게임을 아주 안 하는 것은 아니지만 모바일 게임에 투자하는 시간은 아깝다고 느껴져요. 물론 개인적인 생각입니다.록타르.. 피바람을 몰고올 Mark여..그러면 Mark가 생각하기에 게임으로서 ‘와우’는 어느 정도 레벨인가요?제가 알고 있는 게임들 중 와우는 Top3에 듭니다. 물론 생각을 깊게 해 본 적은 없어서 나머지 2개에 뭘 넣어야 할지 고민해야겠지만 와우는 정말 잘 만든 수작이에요.이제 곧 휴가철이잖아요. 부산 여행 추천 장소 좀 해주세요.외지 사람들은 보통 해운대 많이 가는데, 사실 부산 사람들은 해운대를 잘 안가요. 사람이 너무 많잖아요? 부산 여행 장소를 찾으신다면 개인적으로 을숙도를 추천하고 싶어요. 여긴 가족 단위 여행객이 많은 곳인데요. 서울 사람들이 한강을 찾듯 부산 사람들은 을숙도를 찾아요.이번 여름 휴가는 을숙도로!을숙도? 섬인가요?네, 섬이긴 한데 엄청 큰 다리로 육지와 연결되어 있어서 차를 타고 들어갈 수 있는 곳이에요. 공원이 잘 조성되어 있어요. 자전거도 빌려 탈 수 있고 까페도 있어서 여행 장소로는 딱이에요.축구장도 엄청 많아서 축구 동호회 분들이 자주 찾으시는데요. 사람으로 북적거리지 않는 부산 여행지를 찾는다면 이번 여름 여행은 을숙도로 가보세요. 참고로 을숙도에는 음식점이 많지 않아요. 저 같은 경우, 을숙도 갈 때마다 도시락을 챙겨가곤 합니다.다음은 맛있는 인터뷰의 고정 코너 ‘어서 말을 해’입니다. Jinho가 남긴 질문 ‘잔디를 한문장으로 표현한다면?’에 대해 답을 주신다면?잔디란 ‘기회’ 입니다. IT 업에서 제가 어디까지 능력을 발휘할 수 있을지 확인해볼 수 있는 좋은 기회이기 때문이죠. 좀 진부한가요?전~혀 진부하지 않아요. 멋진 답변을 주셨으니 다음 인터뷰이를 위해 질문 하나 남겨주시겠어요?저는 이걸 꼭 물어보고 싶어요. ‘최근 3년 동안 당신에게 가장 행복했던 일은?’Mark와 개인적으로 얘기를 나눠보고 싶었는데 이렇게 소원이 이뤄졌네요. 개인적으로 뿌듯한 인터뷰였습니다.감사해요. 잘 좀 편집해 주세요.#토스랩 #잔디 #JANDI #개발팀 #개발자 #개발 #팀원소개 #팀원인터뷰 #팀원자랑 #기업문화 #사내문화 #조직문화
조회수 1258

안드로이드 개발자의 고민 Fragment (2)

이전 글 보기: 안드로이드 개발자의 고민: Fragment이번 글에서는 Fragment stack 관리와 Fragment 데이터 Lifecycle 관리 이슈를 줄일 수 있는 해결 방법을 찾아보겠습니다. 이전 글에서는  Fragment를 하나의 View로 관리하는 오픈소스를 검토했었습니다.하지만 검토하는 중에 기존 오픈 소스의 변경과 버전업 관리 이슈의 문제를 그냥 넘어갈 수는 없었습니다. 상용 소스에 바로 적용하기에는 리스크가 크다고 판단해 좀 더 신뢰할 수 있는 방법을 선택하기로 했는데요.요즘은 작년 6월에 Google IO 에 발표한 AndroidX의 내용을 다시 검토하고 있습니다. Deeplink를 통한 목적 화면과 Fragment 스택 관리가 중요한데, 이 기능을 도와주는 것이 AndroidX Navigation이기 때문입니다. 화면 전환을 UI 기반으로 사용하여 화면 관리를 용이하게 만들었습니다. 물론 코드 기반에 익숙한 저는 적용하는데 시간이 걸렸죠.기존의 Fragment 관리는 FragmentManager를 통하여 개발자가 직접 코드 상에서 관리했습니다. 하지만 Navigation의 경우에는 아래와 같이 직관적으로 설정할 수 있습니다.firstFragment -> secondFragment -> thirdFragment 로 화면 간의 흐름을 설정합니다. 하나의 Navigation 파일은 하나 이상의 Activity 에서 사용할 수 있습니다.이 방식은 오히려 현재 사용하는 브랜디 소스와 비슷합니다. 하나의 Activity에 ActivityFragment를 만들어서 1:1 매핑으로 화면을 Fragment를 관리하는 방식과 유사합니다. Navigation 의 세부내용은 Google Developers에서 확인할 수 있습니다.Deeplink 를 통한 Fragment Stack 관리도 간단합니다.Notification 또는 Serice 등에서 PendingIntent를 사용하여 테스트할 수 있습니다. Navigation Fragment stack 순서대로 화면을 쌓은 다음 최종 destination Fragment 로 도착합니다. 이와 같은 방법으로 Push를 통한 화면 관리를 쉽게 할 수 있습니다. 이 내용은 여기에서 자세히 확인할 수 있습니다.신속한 마무리기존 Android 에서 화면 관리가 불편했다면 Navigation으로 직관적이고 쉽게 화면을 관리할 수 있을 겁니다. 브랜디는 아직 적용할 준비 중이지만, 꼭 kotlin과 Navigation을 적용해보려 합니다. 그럼 다시 개발의 숲으로 들어가보겠습니다.글고재성 과장 | R&D 개발1팀[email protected]브랜디, 오직 예쁜 옷만
조회수 2417

CloudWatch에 대하여

OverviewAmazon Web Services(AWS)는 많은 고객들이 이용하고 있습니다. AWS를 이용하여 프로젝트를 운영하고 있다면 각종 서비스의 리소스를 모니터링 하는 게 귀찮게 느껴질 수 있습니다. 이번 글에서는 AWS 리소스를 효과적으로 모니터링할 수 있는 Cloudwatch 서비스를 소개하겠습니다.Cloudwatch는 통합 뷰를 확보하는데 필요한 데이터를 제공합니다. 뿐만 아니라 이벤트 및 리소스를 이용해 경보를 생성할 수도 있습니다.1. Events2. Logs3. Custom Metrics(맞춤형 지표) 생성하기4. Alarm 생성5. Dashboards쉬어가기: Query 언어가 지원하는 여섯 가지 명령 유형1. EventsCloudWatch Events는 정기적인 일정에서 트리거(trigger)되는 규칙을 생성할 수 있습니다.1.규칙 생성을 클릭합니다.2.대상을 호출할 일정을 설정합니다.호출 방식에는 이벤트 패턴과 일정 두 가지가 있습니다. 이벤트 패턴은 json 구조로 표현됩니다. AWS 서비스에서 발생하는 패턴과 일치하면 트리거가 동작합니다. 일정은 지정한 시간과 일치하면 트리거가 동작합니다.cron 또는 rate 표현식을 사용해 예약된 모든 이벤트는 UTC+09:00 시간대를 사용합니다. 최초 단위는 1분입니다.아래는 각각의 필드에 대한 일정 cron식 설명입니다.이번 예제에서는 특정 시간에 트리거되는 일정으로 설정하겠습니다.매일 4시에 동작하도록 설정19 + 9(UTC) - 24(하루) = 새벽 4시3.대상 추가를 선택해 호출할 대상을 지정합니다.Lambda 함수 외에 여러 서비스를 선택할 수 있지만 이번 예제에서는 Lambda 함수를 지정하여 구성하겠습니다.4.규칙의 이름과 설명을 등록하고 규칙 생성을 클릭합니다.5.규칙이 생성된 것을 볼 수 있습니다.2. LogsCloudWatch Logs는 운영 중인 애플리케이션 리소스를 기록하고 액세스할 수 있으며, 관련된 로그 데이터를 검색할 수도 있습니다.1.생성된 규칙이 지정된 시간에 동작하면 CloudWatch Logs에 로그 그룹이 생성된 걸 확인할 수 있습니다.2.Lambda 함수에서 실행된 로그 메시지를 확인할 수 있으며 필터링도 가능합니다.3.로그 그룹에 이벤트 만료 시점을 설정해 오래된 데이터는 모두 자동으로 삭제되도록 설정할 수 있습니다.3. Custom Metrics(맞춤형 지표) 생성하기모니터링하고자 하는 통계치를 직접 선정하고, CloudWatch로 보내 관리하는 지표를 생성해보겠습니다.1.Log Groups에 대한 지표를 생성하겠습니다. 해당 Log Groups에 ‘Filters’를 클릭합니다.2.’Add Metric Filter’를 클릭합니다.3.로그 지표에 대한 필터 패턴을 정의합니다.Filter Pattern* “INFO Success 200” → 세 단어를 모두 포함하는 로그 이벤트 메시지와 일치* “INFO - Start - End” → ‘INFO’ 포함된 메시지 중에 ‘Start’, ‘End’ 제외된 필터 로그 이벤트 메시지와 일치4.필터 및 지표 정보를 입력한 후 ‘Create Filter’를 클릭합니다.Metric Details* Metric Namespace → CloudWatch 지표에 대한 대상 네임 스페이스* Metric Name → 모니터링된 로그 정보가 게시되는 CloudWatch 지표의 이름* Metric Value → 일치하는 로그가 발견될 때마다 지표에 게시하는 숫자 값* Default Value → 일치하는 로그가 발견되지 않은 기간 동안 지표 필터에 보고되는 값5.두 가지 케이스의 필터를 생성했습니다.4. Alarm 생성단일 CloudWatch 지표를 감시하거나 CloudWatch 측정치를 기반으로 하는 수학 표현식의 결과를 감시하는 CloudWatch 경보를 생성할 수 있습니다. 지표가 지정된 임계값에 도달하면 자동으로 이메일을 보내는 Alarm을 만들어보겠습니다.1.추가된 지표 필터에 ‘Create Alarm’ 버튼을 클릭해 경보를 추가합니다.2.경보 세부 정보 및 수행할 작업을 정의합니다.경보 평가경보를 생성할 때, CloudWatch가 경보 상태를 변경하는 조건 세 가지에 대한 설정을 지정할 수 있습니다.기간은 경보에 대해 개별 데이터 포인트를 생성하기 위해 지표 또는 표현식을 평가하는 기간입니다. 초로 표시됩니다. 1분을 기간으로 선택하면 1분마다 하나의 데이터 포인트가 생성됩니다.Evaluation Period(평가 기간)는 경보 상태를 결정할 때 평가할 가장 최근의 기간 또는 데이터 포인트의 수입니다.Datapoints to Alarm(경보에 대한 데이터포인트)는 평가 기간에 경보가 ALARM상태에 도달하게 만드는 위반 데이터 포인트의 수입니다. 위반 데이터 포인트가 연속적일 필요는 없습니다. Evaluation Period(평가 기간)와 동일한 마지막 데이터 포인트의 수 이내면 됩니다.3.경보가 발생할 Alarm 상태와 알림 받을 이메일을 등록합니다.경보 상태/OK/ 지표 또는 표현식이 정의된 임계값 내에 있습니다./ALARM/ 지표 또는 표현식이 정의된 임계값을 벗어났습니다./INSUFFICIENT_DATA/ 경보가 방금 시작되었거나, 측정치를 사용할 수 없거나, 또는 측정치를 통해 경보 상태를 결정하는데 사용할 충분한 데이터가 없습니다.4.이메일 수신함에서 ‘AWS 알림 - 구독 확인’이라는 제목의 메일을 클릭합니다. 내용에 포함된 링크를 클릭해 알림을 수신할 것을 확인합니다. (AWS는 확인된 주소로만 알림을 전송할 수 있습니다.)5.이메일 수신함을 확인해 ‘Confirm subscription’을 클릭합니다.6.등록한 이메일이 확인되었습니다.7.AWS에 이메일이 정상적으로 등록되었는지 SNS Subscriptions 메뉴에서 확인합니다.8.Lambda를 실행해 Alarm 상태를 변경해보겠습니다.9.등록한 이메일 주소로 Alarm 메일이 도착했습니다.5. DashboardsCloudWatch를 통해 리소스를 손쉽게 모니터링할 수 있는 맞춤형 통계 기능입니다.1.Metric Filter에서 추가된 Custom Namespaces를 클릭합니다.2.생성된 Metrics를 선택한 후, Graphed metrics Tab을 클릭합니다.3.Metrics에 표시될 그래프를 설정합니다.1)그래프 제목 : testLambda12)그래프 표시 : 숫자3)그래프 라벨 : testMetrics-400, testMetrics-2004)통계 : 합계5)기간 : 1 Day4.수식을 응용하여 여러 형식의 Metrics 표현식을 추가하겠습니다.지표 수식 함수* METRICS() : 요청에 모든 지표를 반환* SUM(METRICS()) : 모든 지표의 합계* AVG(METRICS()) : 모든 지표의 평균* MIN(METRICS()) : 모든 지표의 최소값* MAX(METRICS()) : 모든 지표의 최대값* ABS(METRICS()) : 각 요소의 절대값* RATE(METRICS()) : 각 요소의 초당 변경 비율5.완성된 지표 Source를 복사합니다.{ "metrics": [ [ { "expression": "SUM(METRICS())", "label": "합계", "id": "e1", "stat": "Sum", "period": 86400 } ], [ { "expression": "AVG(METRICS())", "label": "평균", "id": "e2", "stat": "Sum", "period": 86400 } ], [ { "expression": "MIN(METRICS())", "label": "최소값", "id": "e3", "stat": "Sum", "period": 86400 } ], [ { "expression": "MAX(METRICS())", "label": "최대값", "id": "e4", "stat": "Sum", "period": 86400 } ], [ { "expression": "SUM(METRICS())/SUM(m1)", "label": "SUM(METRICS())/SUM(m1)", "id": "e5", "stat": "Sum", "period": 86400 } ], [ { "expression": "SUM(100/[m1, m2])", "label": "SUM(100/[m1, m2])", "id": "e6", "stat": "Sum", "period": 86400 } ], [ "testMetrics", "testMetrics1", { "id": "m1", "stat": "Sum", "period": 86400, "label": "testMetrics-400" } ], [ ".", "testMetrics2", { "id": "m2", "stat": "Sum", "period": 86400, "label": "testMetrics-200" } ] ], "view": "singleValue", "stacked": false, "region": "ap-northeast-1", "title": "testLambda1", "period": 300 } 6.Dashboard name을 입력한 후 ‘Create dashboard’를 클릭합니다.7.’Add widget’을 클릭해 Number 유형을 선택합니다.8.Source Tab에서 복사해 둔 지표 Source를 붙여 넣고, ‘Create widget’을 클릭합니다.9.위젯이 추가되었습니다. 추가된 위젯은 ‘Save dashboard’ 버튼을 클릭해야 최종 저장됩니다.10.이번에는 로그 메시지 결과를 확인할 수 있는 Query result 유형을 추가해보겠습니다. 먼저 Query result 유형을 선택합니다.11.로그 메시지에 조건을 추가해 필터링합니다.잠시 쉬어가기!: Query 언어가 지원하는 여섯 가지 명령 유형fields : 지정한 필드를 검색합니다. 필드 명령 내에서 함수 및 연산을 사용할 수 있습니다. 만약 @ 기호, 마침표(.) 및 영숫자 문자 이외의 문자가 포함된 로그 필드가 쿼리에 명명되어 있으면 해당 필드 이름은 억음 기호로 둘러싸야 합니다.filter : 하나 이상의 조건으로 필터링합니다. filter statusCode like /2\d\d/ → 필드 statusCode의 값이 200~299인 로그 이벤트를 반환합니다.stats : 로그 필드에 대한 지정된 시간 간격의 집계 통계를 계산합니다.sort : 검색된 로그 이벤트를 정렬합니다.limit : 쿼리에서 반환되는 로그 이벤트 수를 제한합니다.parse : 로그 필드에서 데이터를 추출하고 쿼리로 추가 처리할 수 있는 임시 필드가 하나 이상 생성됩니다.12.추가된 위젯은 이름과 사이즈를 조절한 후, ‘Save dashboard’ 버튼을 클릭해 최종 저장합니다.13.생성한 Alarm을 Dashboard에 추가하겠습니다.14.Dashboard가 완성되었습니다!Conclusion지금까지 CloudWatch 서비스를 소개했습니다. 이 서비스를 이용하면 로그와 지표를 쉽게 시각화할 수 있고, 작업을 자동화할 수도 있는 것을 확인했습니다. CloudWatch를 이용해 애플리케이션을 최적화하고, 원활하게 실행해보는 건 어떨까요. 분명 리소스를 효과적으로 다룰 수 있을 겁니다.글곽정섭 과장 | R&D 개발1팀[email protected]브랜디, 오직 예쁜 옷만
조회수 1232

머신러닝 엔지니어 정갑님을 소개합니다

같이 일하고 있는 직장 동료들에 대해 얼마나 알고 계시나요? 엑스브레인처럼 작은 팀의 경우에는 함께하는 한 분 한 분이 팀 전체 분위기에 끼치는 영향이 상당하답니다. 또한, 머신러닝 툴 ‘다리아’로 저희가 꿈꾸는 데이터 사이언스계의 변혁을 일으키려면, 이를 위해 일하는 팀 또한 서로 잘 알고, 협력할 줄 알아야겠죠.각각 개성이 넘치지만, 서로 모여 엑스브레인의 매일매일을 풍족하고 즐겁게 만들어가는 팀을 소개합니다! 각 멤버들의 일상과 엑스브레인에서의 직무에 대해서도 알아보고, 또 뉴욕타임즈에 실린 “상대방과 사랑에 빠질 수 있는 36가지 질문” 중 직장 동료에게 할 수 있을 만한, 가장 흥미로운 질문들을 추려서 진행한 인터뷰를 통해 엑스브레인 팀 멤버 개개인의 색다른 매력을 만나보세요.(그렇다고 진짜로 사랑에 빠지시면 곤란합니다…)가장 최근 엑스브레인 팀에 합류하신 정갑님은 따뜻하고 밝은 산타 클라라에서 서서히 동결 준비 중인 서울로 오셨답니다 (감기 조심하세요…). 그래도 석박사 시절을 이보다 훨씬 춥고 눈에 갇히기 일쑤인 미시건에서 보내셨다고, 추위에는 강하다고 하시네요. 머신러닝 엔지니어로서 다리아의 엔진을 위한 개발 작업을 하시는 정갑님은 여가시간엔 반려묘 졸리와 브래드와 함께하거나, 요리나 등산을 즐기시기도 한답니다. 정갑님을 만나보세요!Fun Fact: 정갑님은 팀 멤버 중 가장 아침 일찍 출근하신답니다안녕하세요 정갑님! 엑스브레인에서의 역할에 대해서 얘기해주세요정갑: 머신러닝 엔지니어로 입사를 했고, 머신러닝 엔진을 개발하는 것이 주요 업무입니다. 많은 사람들이 머신러닝을 쉽게 쓰기 위해서는 현 상황에서 어떤 기술들과 어떤 문제점이 있는지 알아내야 하고, 저는 그 문제들을 해결하기 위한 중요한 기술을 찾아서 연구를 하고 해결 방안을 찾는 역할을 하고 있습니다어떤 계기로 머신러닝 엔지니어가 되셨나요?정갑:대학원, 회사에서 연구를 하면서 머신러닝의 사용자 입장이었는데, 사용하고 이해하는 과정이 상당히 어려웠어요. 기존에 나와있는 툴들도 사용성이 좋지 않았고…이런 과정을 제가 직접 개선하면 좋을 것 같아서 머신러닝 엔지니어로서 엑스브레인 팀에 들어오게 되었습니다.왜 엑스브레인인가요?정갑: 일단 조직의 인력구성이 마음에 들었고, 팀원들의 역량과 조직문화가 제가 원하는 분위기여서 좋았습니다. 두번째는 엑스브레인이 추구하고자 하는 가치 — 머신러닝이란 기술에 대해서 갖고 있는 생각 — 이 제가 평소에 갖고 있던 생각과 일치해서요…머신러닝을 단순히 이윤 추구의 수단으로 생각하는게 아니라, 이걸 더 많은 사람들이 이용해서 가치를 찾게 하자는 뜻이 좋았어요. 또, 초창기 회사에서 한 번 어떻게 조직이 커가고, 함께 성장하는 경험을 해보고 싶기도 했고요. 그리고 주변 신뢰할 만한 분들에게서 엑스브레인에 대한 좋은 이야기도 많이 들었어요.보통 하루 일과가 어떻게 되나요?정갑:아침 9시 15분 쯤에 도착합니다. 밤새 와 있던 슬랙 메시지와 이메일을 체크하고, 커피 한 잔을 마십니다. 아침엔 집중이 잘 되니까 읽어봐야 될 논문이나 자료 등을 보고, 또 제가 머신러닝을 전공하지는 않았으니까 아직 따로 공부해야 될게 많기 때문에 그 부분에도 신경쓰고 있어요. 머리가 워밍업이 되면 기존에 짜여있던 코드를 보고, 개발할 부분이 있으면 개발을 합니다. 점심시간이 되면 점심을 같이 먹기도 하고요 (미국에 있을 때는 따로 점심 시간을 내서 팀원들끼리 대화할 기회가 없었기 때문에, 엑스브레인의 이런 문화가 좋습니다). 연구개발과 미팅의 연속이죠. 오늘은 현재 머신러닝 엔진에 문제가 있어서 그 이슈를 뜯어보았는데, 그 과정을 바탕으로 어떻게 해결할 것인지에 대한 아이디어를 구현하는 과정을 거쳤습니다. 구현과 테스팅과 trial and error을 앞으로 몇 주간 반복할 것 같아요.정갑님의 직무 중 가장 즐기는 일은?정갑:무언가를 향상시키는 것? 이렇게 고치면 좋아질 것 같은데…라는 생각을 가지고 일하는 게 좋습니다. 저희 기존 시스템을 향상시키는데도 관심이 있지만, 롱텀으로 봤을 땐 엑스브레인만의 유니크한 기술을 가져야 하기 때문에 그 기술이 뭔지 알아내고, 개발하고, 사용자들의 니즈를 파악하는 것에 관심이 있습니다. 그래서 시스템의 문제를 찾으려고 많은 시간을 생각하는데 투자하고 있죠.반대로, 가장 하기 싫거나 어려운 일은?정갑:어려워서 하기 싫다기보다는… 풀어야 할 문제를 찾는 거 자체가 어려운 것 같아요. 이럴 땐 네 가지 상황이 있는데, 이미 찾은 문제, 풀수 없는 문제, 너무 쉬워서 관심이 없는 문제, 그리고 풀수 있고 임팩트 있는 문제가 있죠. 저희는 그 마지막 예를 찾으려고 하는 거고요. 그 과정이 힘들긴 하지만 즐기고 있습니다.정갑님 책상에 있는 물건 중 정갑님을 가장 잘 대변한다고 생각하는 아이템은?정갑:딱히 책상에 물건을 두지는 않는데… 미국에서 일하던 시절 실리콘밸리에서 여러 유명한 회사들 (트위터, 링크드인 등등) 구경을 했는데 엔지니어들은 대부분 책상에 컴퓨터 하나만 있고 다른 장식이 없더라고요. 저는 그런 단순함이 좋았어요.엄청난 집중력을 발휘하시기도 하죠최근에 합류한 멤버로서, 정갑님이 생각하시는 엑스브레인의 비전을 말해주세요.정갑:비전이라기보다는 나아가야 할 방향 같은 건데, 지금은 머신러닝에 대해서 사람들이 굉장히 많은 이야기를 하지만, 차분하게 앉아서 연구와 기술개발을 해야 할 시점이라고 생각합니다. 롱텀으로 긴 안목을 갖고서 차근차근하게 기초단계를 밟아나가는, 유행에 휩쓸리지 않고, 기본에 충실한 엑스브레인이 되었으면 좋겠어요.씨네마 소사이어티 때 추천하고 싶은 영화가 있다면?정갑:맷 데이먼 주연의 Downsizing…개봉하면 팀 멤버들과 같이 보고싶네요. 끝나고 토론할 주제가 많을 것 같아서요.10년 뒤 지금, 정갑님은 어떤 모습일까요?정갑: 앞으로의 10년 동안 공부를 해서 제대로 된 머신러닝 엔지니어가 되고 싶어요. 지금은 초기 엔지니어지만, 그때는 좋은 개발자들을 발굴해 내서 성장하는데 도움도 줄 수 있는 시니어 급 엔지니어가 되고 싶습니다.내가 생각하는 엑스브레인의 “엑기스”를 세 단어로 말한다면?정갑:진지와 엉뚱함의 공존?엑스브레인의 어떤 멤버와도 저녁 식사를 할 수 있다면, 누구와 같이 먹고 싶나요?정갑:진영님. 같이 점심을 먹어본게 입사했을 때, 수요미식회 때 빼고는 없어서... 진영님과 대화할 기회가 별로 없었는데 재밌는 분일 것 같습니다.이 세상 어느 누구와도 저녁 식사를 할 수 있다면, 누구와 같이 먹고 싶나요?정갑:칼 세이건? 그분의 책을 읽고 어렸을 때 가졌던 우주에 대한 여러가지 동경을 되살려 보고 싶네요… 과학에 대한 열정을 다시 느끼고 싶기도 하고.유명해지고 싶나요? 어떤 방법으로요?정갑:아니요.정갑님에게 “완벽한” 날이란 어떤 날인가요?정갑:아직 오지 않은 내일이…아닐까요? 너무 엉뚱한 대답인가요?90살까지 살 수 있고 마지막 60년을 서른 살의 마음, 혹은 서른 살의 몸으로 살 수 있다고 해봅시다. 몸과 마음 중 어느 쪽을 택할 건가요?정갑: 몸. 마음은 성숙하지만, 몸은 퇴화하니까…정갑님의 인생에서 가장 감사하게 생각하는 것은 무엇인가요?정갑:건강함인 것 같아요.내일 아침 눈을 떴을 때 어떤 능력이나 특성을 가지게 된다면 어떤 것이었으면 좋겠어요?정갑:무언가를 읽고 이해하는데 오래 걸리는 편인데, 이해력이 빨라지면 좋겠습니다. 두뇌회전도 빨라지고…지금까지 정갑님 인생에서 가장 잘해낸 일은 무엇인가요?정갑:좋은 사람과 인연을 맺은 일인 것 같아요.엑스브레인에서 가장 기억에 남는 일이 뭔가요?정갑:오늘 인터뷰…? (하하하)혹시 농담의 대상으로 삼아서는 안 된다고 생각하는 것이 있다면 어떤 것들이 있을까요?정갑:듣는 대상에 따라 다르겠지만, 사람들의 약점에 대해서는 농담을 하지 말아야 한다고 생각합니다.정갑님의 모든 것이 있는 집이 불에 타고 있습니다. 가족들을 다 구한 후 마지막 한 가지를 가지고 올 수 있습니다. 어떤 것을 가지고 나올 건가요?정갑:하드 드라이브! 제 모든 사진과 파일이 담겨 있거든요.#엑스브레인 #팀원소개 #팀원인터뷰 #기업문화 #조직문화 #팀원자랑 #머신러닝 #머신러닝엔지니어
조회수 2292

디너의여왕 탐구 생활_인터뷰2. 개발팀

안녕하세요 :)오늘은 "디너의여왕 탐구생활"개발팀 편을 들고 왔습니다.개발팀 열일 현장입니다.무슨 뜻인지 모를 단어들이컴퓨터에 가득가득하네요!이제 그들과 인터뷰를 진행하면서본격적으로 파헤쳐 보도록 하겠습니다!!!오늘 인터뷰는 개발팀의 3인가디님, 월리님, 펭돌이님과인터뷰를 진행해보겠습니다 :-)첫번째 인터뷰는개발팀 가디님과 진행하겠습니다.Q. 현재 담당하고 계신직무에 대해 소개 부탁드려요. A. 저는 디너의여왕에서데이터 수집과Elasticsearch와 관련된검색시스템을 담당하고 있습니다.  Q. 어떤 동기를 갖고해당 직무에 지원하게 되었나요? A. 개인 프로젝트로기본적인 검색엔진 시스템을구축해 본 적이 있었는데,해당 경험을 살릴 수 있는소중한 기회라 생각해서해당 직무에 지원하게 되었습니다.Q. 해당 직무에 필요한 역량이 있다면무엇일까요?  A. 검색 시스템의전체적인 흐름을 아는 것이아무래도 업무를 수행하는데 도움이 됩니다.그리고 관련된 자료가 한국어로는 흔하지 않기 때문에필요한 자료들을 잘 찾을 수 있는스킬이 필요할 것 같습니다.Q. 해당 직무에서 일할 때 사용하는자신만의 스킬, 노하우가 있다면 무엇인가요? A. 직무와 관련된 자료는아무래도 영문이 많은데다행히 제가 익숙한 일본어로도양질의 자료가 있어서자료를 얻는데 도움이 되고 있습니다.Q. 해당 직무에서 일하면서 즐거웠던 적,힘들었던 적이 있다면 언제일까요?  검색과 관련된 기능은 Elasticsearch에서많은 것을 처리해 주기는 하지만여전히 개발자가 직접 처리해 주어야 하는작업들이 있습니다.다소 지루하게 느껴질 수 있는 부분이지만시행착오를 겪으면서조금씩 개선이 되는 시스템을 보면서보람을 느낄 수 있었습니다.두 번째 인터뷰는개발팀 월리님과 진행하겠습니다.Q. 현재 담당하고 계신 직무에 대해소개 부탁드려요.  디너의여왕 웹 프론트엔드 개발을맡고있습니다.Q. 어떤 동기를 갖고해당 직무에 지원하게 되었나요?디자인을 직접 코딩해서나오는 표현이 재밌어서 시작했는데마침 타이밍 맞게 여기에 기회가 생겨서요.Q. 해당 직무에 필요한 역량이 있다면 무엇일까요?  기본적인 html/ css/ javascript에 대한기본적인 이해가 일단 필요하고요,프론트엔드 분야가 일반적으로가장 노출이 많이 되는 부분이다 보니일반적으로 개발만 하는 것보다는UX/UI에 대한 고민하는 자세가가장 중요한 것 같습니다.  Q. 해당 직무에서 일할 때 사용하는 자신만의 스킬, 노하우가 있다면 무엇인가요?  저도 부족한데 뭐…코딩은 왕도가 없습니다.일단 많이 뜯어고쳐보고또 삽질도 많이 해봐야 한다고 생각합니다.  그러다 보면 자연스럽게 익혀져서나만의 노하우가 생긴다고 보면 됩니다!Q. 해당 직무에서 일하면서 즐거웠던 적,힘들었던 적이 있다면 언제일까요?  프론트엔드 개발자로서내가 만든 코드가실제 서비스에 나온다는 것 자체가보람찬 일입니다.힘든 건 묻지 마세요Q. 마지막으로, 디너의여왕이 될지원자들에게 한 마디 부탁드려요. 어솨요 반가버요 ヽ(‘ ∇‘ )ノ세 번째 인터뷰입니다.개발팀 펭돌이님과 함께 진행하겠습니다!Q. 현재 담당하고 계신직무에 대해 소개 부탁드려요.  A. 안녕하세요.저는 디너의여왕에서 사용되는웹 서비스 백엔드를 개발하고 있어요.  Q. 어떤 동기를 갖고 해당 직무에지원하게 되었나요?  A. 실시간 트래픽이 높은 웹 서비스를개발해보고 싶은 욕심이 있었어요.트래픽이 높으면 신경 써야 할 것들이여러 가지가 있는데그것 또한 경험이 되리라고 생각했습니다.  또, 과거에잠시 블로그를 운영했던 적이 있었는데그 덕분에,  SNS 블로그 마케팅이라는세일즈 프로모션에도 관심이 많았어요.Q. 해당 직무에 필요한 역량이 있다면무엇일까요?  A. 한 가지 이상의 서버에서 사용되는프로그래밍 언어를 다룰 줄 알아야 합니다. 또 데이터를 수집하고,가공하는 등의 기술에 대해서도응용력이 좋아야 합니다.  그 외에도 다양한 요구 사항들이동시다발적으로 발생할 수가 있으니우선순위에 따라업무를 순서대로 처리할 수 있는 능력이중요한 것 같아요.Q. 해당 직무에서 일할 때 사용하는자신만의 스킬, 노하우가 있다면무엇인가요?  A. 저는 최대한 오픈 소스,검색을 활용하는 편이에요.  오픈 소스 같은 경우에는여러 포럼, 저장소 등에서 검색해보는 것이중요하고,검색 같은 경우에는적절한 키워드 (영어 의문문 how to ~)를이용하여 검색하면웬만한 지식들은 구글에 나와 있습니다.Q. 해당 직무에서 일하면서 즐거웠던 적,힘들었던 적이 있다면 언제일까요?  A. 갑작스럽고 치명적인 오류 등에 의해서갑자기 바빠지거나,예상치 못한 오류 때문에업무에 지장이 생기는 경우가가장 스트레스를 많이 받았던 것 같아요.최대한 그런 일들이 발생하지 않도록예방해요.집을 짓는다고 가정하면초석부터 탄탄히 짓는 것이죠.즐거운 일은아무래도 예상외로 술술 풀려나갈 때가장 보람찬 것 같아요.Q. 개발 업무의 매력은 어떤 것이 있을까요? A. 개발 업무는인터넷이라는 가상의 공간에서무언가를 창조하고,사람들에게 보여주는 매력이 있는 것 같아요.  또, 만들어진 결과물로 인해서누군가의 인생을좌우할 수 있을 것만 같아요.이런 게 매력이 아닐까요? Q. 마지막으로,디너의여왕이 될 지원자들에게한 마디 부탁드려요. A. 디너의 여왕은단순한 음식점 소개 웹 사이트가 아닌,푸드 플랫폼을 위한다양한 기술들이 집약되어 있습니다.단순히 포스트를 올리고,보여주는 것이 아닌어떻게 하면 효율적인 마케팅 효과를 불러올 수 있는 것인지 수집하고 가공하는복잡한 기술들이 집약되어 있습니다.  빅데이터 등의 IT 패러다임에관심이 있으시다면서로 win-win할 수 있는 기회가 될 것 같아요.이상으로 인터뷰를 마치겠습니다 :-)디너의여왕 탐구생활 다음 편은누구와 함께 하게 될까요?#디너의여왕 #개발팀 #팀원소개 #팀원인터뷰 #기업문화 #조직문화
조회수 1481

Semantic Versioning 소개

Semantic Versioning 소개Versioning?소프트웨어 개발 생태계는 수많은 사람들이 서로의 기술과 성과를 이어받아 오며 믿을 수 없는 수준의 협력 체제를 구축해오고 있습니다. 의존성은 이러한 협력체제에서 나오게 된 요소로, 다른 사람들이 만들어온 기능을 다시 만들 필요 없이 손쉽게 가져와서 재활용하는 방식으로 빠르게 소프트웨어를 만들 수 있게 되었습니다.하지만 이렇게 여러 사람에게 이용되는 패키지가 새롭게 업데이트될 때, 생각보다 다양한 문제에 직면하게 되었습니다. 기능의 사용법을 바꾸어버리거나 동작 방식의 변경 같은 변화들은 그에 의존하는 다른 소프트웨어를 의도대로 동작하지 못하게 하므로, 새로운 변화와 기존의 것을 구분할 필요가 생겼습니다. 버전이라는 개념은 이러한 패키지의 변화를 구분하기 위해 사용하기 시작하였습니다.Semantic Versioning?버전이라는 코드 형태의 구분방식은 많은 핵심 문제를 해결해주었지만, 아직 여러 과제가 남아있었습니다. 버전 명의 작성 방식에 관한 기준이 패키지마다 제각각 다른 것이 문제였습니다. 0.x와 1.x의 차이, 1.0.0 혹은 1.000. 선행 배포와 정식 버전의 구분 방법 등 모든 소프트웨어, 패키지는 저마다의 기준을 가지고 있었으며, 이는 어느 정도의 적당한 공통점이 있었지만, 그 점이 미묘하게 모두 차이가 있어 버전에 따른 의미 해석을 어렵게 하였습니다.Semantic Versioning은 Github의 공동창업자인 Tom Preston-Werner가 위의 문제를 해결하기 위해 기존의 현안을 모아 만든 제안입니다. 스펙 문서는 RFC 2119에 의해 규칙을 표기하여 의미적 엄격함을 높이고, 패키지 개발 생명주기에 발생할 수 있는 여러 상황을 포괄적으로 담아 일관성과 유연성을 균형 있게 갖추고 있습니다.규칙다음은 Semantic Versioning(v2.0.0-rc1)의 스펙을 한국어로 번역한 내용입니다.1. Semantic Versioning을 쓰는 소프트웨어는 반드시 공개 API를 정의해야 한다. 이 API는 코드 자체에 정의되어 있거나 명시적으로 문서화 되어있어야 한다. 이 과정은 포괄적이며 정확해야 한다.2. 일반 버전 명은 반드시 X.Y.Z 형태를 보여야 하며 X, Y, Z는 음이 아닌 정수이다. X는 주요한 버전이며, Y는 작은 버전, Z는 패치버전이다. 각 요소는 1씩 차례로 증가해야 한다. 예: 1.9.0 -> 1.10.0 -> 1.11.0.3. 주요 버전 숫자가 올라갈 때, 작은 버전 숫자와 패치 버전 숫자는 0으로 재설정되어야 한다. 작은 버전 숫자가 올라갈 때, 패치 버전 숫자는 0으로 재설정되어야 한다. 예: 1.1.3 -> 2.0.0, 2.1.7 -> 2.2.04. 버전 명이 주어진 패키지가 한번 공개되면, 해당 버전의 내용은 절대 수정되어선 안된다. 어떤 수정도 반드시 새로운 버전으로 공개되어야 한다.5. 주요 버전 0 (0.y.z)은 초기 개발을 위한 것이다. 언제든 변경될 수 있다. 공개 API는 안전하지 않다고 여긴다.6. 버전 1.0.0은 공개 API를 정의한다. 이 공개 이후의 버전 숫자가 바뀌는 방법은 공개 API와 변경 방법에 따라 결정된다.7. 패치 버전 Z (x.y.Zx > 0)는 하위호환을 하지만 버그 수정이 있을 때 올라간다. 버그 수정은 내부적으로 잘못 처리되고 있는 것을 고치는 것을 의미한다.8. 작은 버전 Y (x.Y.zx > 0)는 새로운 기능이 추가되었지만 기존의 공개 API가 하위호환되고 있을 때 올라간다. 공개 API가 하나 이상 deprecated될 시에도 올라가야 한다. 부가적인 새 기능이나 개선이 내부 코드 (private code)에 있을 시에도 올릴 수 있다. 이는 패치 수준의 변화를 포함할 수 있으나, 작은 버전이 올라가면 패치 버전은 꼭 0이 되어야 한다.9. 주요 버전 X (X.y.zX > 0)는 하위호환되지 않는 변화가 추가될 때 반드시 올라가야 한다. 이는 패치 수준과 작은 수준의 변화를 포함할 수 있으나, 주요 버전이 올라가면 작은 버전과 패치 버전은 꼭 0이 되어야 한다.10. 선행 배포 버전은 대시(-)와 점으로 나누어진 식별자들의 묶음을 패치 버전 뒤에 표시한다. 식별자들은 ASCII 영숫자와 대시로만 구성되어야 한다. [0-9A-Za-z-]. 선행 배포 버전은 연관된 일반 버전보다 낮은 우선순위를 가진다.11. 개발 버전은 더하기(+)와 점으로 나누어진 식별자들의 묶음을 패치 버전 뒤에 표시한다. 식별자들은 ASCII 영숫자와 대시로만 구성되어야 한다. [0-9A-Za-z-]. 빌드 버전은 연관된 일반 버전보다 높은 우선순위를 가진다.12. 우선순위는 주요, 작은, 패치, 선행 배포, 빌드 식별자 내 숫자 순으로 계산되어야 한다. 주요, 작은, 패치 버전은 항상 숫자로 비교되어야 한다. 선행 배포와 빌드 버전의 우선순위는 반드시 각 점으로 나누어진 식별자들이 아래 규칙에 따라 비교되어야 한다: 1. 숫자로만 이루어진 식별자는 숫자로 비교 (2) 문자와 대시가 포함된 식별자는 ASCII 정렬 순서대로 비교. 숫자 식별자는 숫자가 아닌 식별자보다 낮은 우선순위를 가진다. 예: 1.0.0-alpha < 1>응용여러 오픈소스 프로젝트들이 이미 Semantic Versioning에 따라 버전 명을 표기하기 시작하였으며, 해당 규칙에 기반을 둔 버전 비교 라이브러리도 만들어지고 있습니다.•node.js: https://github.com/isaacs/node-semver•PHP: https://github.com/GordonSchmidt/SemVer•Python: https://github.com/k-bx/python-semver•Ruby: https://github.com/iantruslove/SemverStringerseaport는 node.js 에서 서비스 클러스터들이 Semantic Versioning에 따라 버전 의존성을 가지게 설계할 수 있어 보다 안정적인 버전 협상이 가능하도록 하고 있습니다.server.js:var seaport = require('seaport');var ports = seaport.connect('localhost', 9090);var http = require('http');var server = http.createServer(function (req, res) {res.end('beep boop\r\n');});server.listen(ports.register('[email protected]'));client.js:var seaport = require('seaport');var ports = seaport.connect(9090);var request = require('request');ports.get('[email protected]', function (ps) {var u = 'http://' + ps[0].host + ':' + ps[0].port;request(u).pipe(process.stdout);});output:$ node server.js &[1] 6012$ node client.jsbeep boop마치며비록 작은 통일일지는 모르나, 버전 명을 작성하는 훌륭한 기준이 있다는 것은 장기적으로 개발 생태계를 더욱 빠르고 긴밀하게 협력하도록 도와줄 것이라 생각됩니다. 의미적 해석이 가능한 코드는 의존성 문제를 더 똑똑한 수준으로 자동화할 수 있기 때문이죠. 버전 명을 지으실 때 좋은 안내서가 되었으면 좋겠습니다.#스포카 #개발 #개발자 #개발팀 #꿀팁 #인사이트

기업문화 엿볼 때, 더팀스

로그인

/