스토리 홈

인터뷰

피드

뉴스

조회수 1753

스켈티인터뷰 / 스켈터랩스의 잡학다이너마이트 변규홍 님을 만나보세요:)

Editor. 스켈터랩스에서는 배경이 모두 다른 다양한 멤버들이 함께 모여 최고의 머신 인텔리전스 개발을 향해 힘껏 나아가고 있습니다. 스켈터랩스의 식구들, Skeltie를 소개하는 시간을 통해 우리의 일상과 혁신을 만들어가는 과정을 들어보세요! 스켈터랩스의 잡학다이너마이트 변규홍 님을 만나보세요:)PART1. About Skelter Labs사진1. 스켈터랩스의 소프트웨어 엔지니어, 변규홍 님Q. 간단한 자기소개를 부탁한다.A. 이름은 변규홍. 스켈터랩스에서 소프트웨어 엔지니어로 일하며, 컴퓨터에게 열심히 한국어를 가르치고 함께 배우고 있다. 대충 20년 전부터 컴퓨터 공부를 시작해서 컴퓨터 관련된 일이라면 사족을 못쓰는 덕후이기도 하다.Q. 현재 스켈터랩스에서 어떤 업무를 맡고있는가.A. 스켈터랩스의 인공지능 대화 엔진 개발 팀인 헤르메스(Hermes)에서 흔히 ‘챗봇’이라 부르는 인공지능 대화 엔진을 만들고 있다. 우리가 만드는 인공지능 대화 엔진은 ‘챗봇을 만들고자 하는 사람들이 누구나 쉽게 챗봇을 만들도록 돕는 편리한 사용'을 목표로 한다. 때문에 비개발자도 이해하기 쉽도록 효율적이고 간편한 UI와 구조로 개발하고 있다. 거기서 나는 어떻게 하면 컴퓨터가 사람이 하는 말을 더 잘 알아듣고 잘 대답할 수 있는지 연구하고 있다. 어떤 처리를 해야하는지, 언어의 어떤 패턴을 인식하는지 등 ‘자연어 처리(Natural Language Processing,NLP)’ 혹은 자연언어처리라고 불리는 기술 전반에 대한 연구를 진행하고 있다.Q. 자연어 처리라는 부분이 생소하다. 언어의 분석이나 처리에 대한 얘기를 더 해줄 수 있나.A. 챗봇 위주로 설명해 보자. 우리가 한국어 문장을 컴퓨터나 스마트폰에 입력할 때, 특히 채팅할 때는 문장의 변화가 심한 편이다. 띄어쓰기를 실수할 수도 있고 급식체같은 축약어를 사용하기도 한다. 같은 의도를 담은 문장이 아주 다르게 표현되는가 하면, 비슷한 문장이 어순이나 표현 한 두 가지만 바뀌어도 전혀 다른 뜻이 되기도 한다. 이러한 인간의 언어를 컴퓨터가 잘 알아들을 수 있도록 분석하고 처리하는 것이다. 입력된 문장에서 어떤 부분이 명사고 어떤 부분이 동사인지를 찾거나, 문장 속에서 어떤 형태소에 집중해야 하는지 분석한다. 그리고 은행 계좌나 전화번호처럼 규칙에 맞는 숫자가 다양하게 입력될 수 있는 경우를 찾아내기도 한다. 이런 과정을 거쳐 사람이 어떤 의도를 갖고 입력한 문장인지, 어떤 정보가 담겨있는지 식별해낼 수 있다.Q. 들어보니 기술에 대한 지식뿐만 아니라 언어학에 대한 조예가 필요한 분야로 보인다.A. 맞다. 이 분야를 전산학(컴퓨터공학)에서는 ‘자연언어처리’라고 하고 언어학에서는 ‘전산언어학(Computational Linguistics)’ 혹은 ‘계산언어학’이라고 한다. 학제 간 학문으로서의 성격이 강한 분야다. 초창기에는 언어학자들이 찾아낸 인간 언어의 구조, 규칙을 컴퓨터공학자 / 전산학자들이 프로그램으로 구현하는 연구가 많았다. 그러다가 애초의 예상보다 인간의 언어 구조가 훨씬 더 복잡하다는 것을 인식한 이후부터는 인간의 언어에서 규칙성을 찾는 과정도 통계적 방법 등을 통해 컴퓨터의 힘을 빌리게 되었다. 최근에는 요즘 화두인 머신러닝 기법을 적극적으로 적용하면서 연구 트렌드가 조금씩 바뀌고 있다. 다양한 규칙에 따라 문장을 분석하기보다, 빅데이터로 정리된 방대한 언어생활 자료를 컴퓨터 스스로 학습하여 문장 속에서 필요한 정보를 찾아내는 식으로의 전환이랄까. 하지만 여전히 좀 더 좋은 결과물을 내려면 언어학에 대한 지식과 규칙성에서 찾아낸 정보들이 필요한 것도 사실이다. 그래서 스켈터랩스에서는 규칙 기반 기법들과 머신러닝 기법 모두를 하이브리드 형태로 결합하여 대화 엔진을 개발하고 있다.Q. 아무리 다양한 형태로 기법을 결합하여 사용하더라도, 엔지니어가 언어학에 대해 연구하기는 쉽지 않아 보인다. 언어학을 별도로 공부하거나 혹은 언어학에 대한 관심을 이전부터 가지고 있었는지.A. 언어학이라기보다는 사실 나는 대학교에서 문학 동아리 활동을 오랫동안 했다. 자연스럽게 다양한 활동을 통해서 문학에 대한 얘기를 하다 보니 언어에 대한 관심도 꽤 높았던 것 같다. 무엇보다 구글코리아의 번역기 개발팀에서 인턴을 하며, 컴퓨터로 인간의 언어를 다루는 것이 굉장히 흥미롭다고 생각했고 꾸준히 관심을 이어왔다. Q. 구글 코리아 인턴 경험이 규홍님에게 여러모로 지대한 영향을 끼친 것으로 알고 있다. 그 얘기를 듣고 싶다.A. 대학에 처음 입학했을 때, 사실 실망감이 더 컸다. 합리적인 의사소통은 막혀있었고, 당시 학교의 학사제도 개편으로 인해 여러모로 시끄러운 상황이었다. 그러던 차에 마침 학교에 구글코리아에서 캠퍼스 리쿠르팅을 왔는데, 선배 중 한 명이 ‘왜 구글은 한국에서 인턴을 채용하지 않습니까' 라고 꽤나 당돌한 질문을 던졌다. 그렇게 구글 코리아 인턴 채용이 열려 면접 기회를 얻게 되었다. 당시 내 이력서에는 대학교 입학 후의 경력이라고는 연극동아리 공연 이력이 전부였기 때문에 일종의 두려움도 컸다. 하지만 일본어로 된 만화책을 컴퓨터에 넣으면 한국어로 번역된 만화책이 튀어나오게 하고, 컴파일(COMPILE) 사의 게임 중 미처 한국어로 번역되지 못한 게임들을 컴퓨터가 알아서 번역해 즐길 수 있게 하는, 그런 컴퓨터 프로그램을 직접 만들고 싶다는 꿈이 더 컸다. 마침 나의 면접관들도 구글 코리아 번역기 개발팀 분들이었다. 그렇게 구글 코리아 번역기 개발팀 인턴으로 입사하게 되었고, 그때의 경험이 나의 꿈의 실현 가능성에 대한 일종의 확신을 주었다.Q. 스켈터랩스에는 어떻게 입사하게 되었나A. 인턴 할 당시의 구글 코리아 사장이 지금 스켈터랩스 창업자, 조원규 대표님이다. 그리고 구글 코리아 면접관이었던 분이 우리 팀의 테크 리더(Tech Leader)를 맡고 있는 이충식 님이기도 하다. 작년 충식 님으로부터 어려운 문제를 풀어야 하는데 같이 한번 풀어보자는 연락을 받았다. 그 문제가 너무 어려울 것 같아서 답장을 망설이고 있었다. 그러다 이전 직장에 대한 염증과 새로운 일에 대한 호기심 등의 마음으로 충식님을 다시 만나 뵈니, 스켈터랩스에서 내가 어렸을 적 꿈꾸던 챗봇을 만들고 계셨다.Q.  스켈터랩스에서의 업무는 이전에 일했던 혹은 알고 있는 다른 개발자의 업무랑 어떻게 다른가. A. 사실 인공지능을 기반으로 한 스타트업에는 뛰어난 사람들이 많은 것 같다. 그러나 스켈터랩스가 다른 회사와 다른 점은 ‘내 동료가 누구인가'에 대한 인식의 범위가 조금 더 넓다는 점이다. 가령 디자이너는 디자이너끼리, 기획자들을 기획자끼리만 협력하고 부서에 따른 책임이나 업무 범위에 대해서 선을 긋는 문화가 흔히 있지 않나. 어떤 직장들은 수직적인 위계 구조를 강요하고 모든 걸 서류로 보고하게 만들기 때문에 일의 효율이 떨어지기도 한다. 그러나 스켈터랩스는 팀 간에, 직무 간에 서로의 업무 영역을 자로 재듯 규정하지 않고 넘나들며, 좀 더 활발한 소통을 추구한다. 덕분에 ‘하나의 공동체'라는 인식을 자연스럽게 가질 수 있다. 서로와 함께 일한다는 것에 대해 우리 스스로 가지는 자긍심도 대단하다. 사내에는 지인을 신규 입사자로 추천하는 채용 제도가 있는데, 그간 내가 일해왔던 회사 중 우리 회사만큼 열심히 지인들에게 추천하는 회사도 없었다. 사실 내가 일하는 회사가 별로면 친구에게 추천도 못 하지 않겠나. 그만큼 서로 만족하고, 자부심을 가지고 일한다는 것을 방증하는 면모인 것 같다.또한 스켈터랩스는 불필요한 서류 업무를 배제하는 대신, 아주 엄격한 코드 리뷰 시스템을 가지고 있다. 내가 과거에 근무했던 회사들은 많은 경우 상대적으로 지금 당장 작동하는 코드를 만들어 내는 것에 집중했다. 물론 이러한 방식이 때로는 실용적이다. 그러나 기능이 잘 작동되는지만 살피다 보니, 숨겨진 버그(Software Bug)가 남겨지고 이것이 뒤늦게 발견되어 더 큰 문제를 일으키기도 했다. 때로는 버그의 존재를 코드 작성자만이 알고 있기도 했다. 이렇듯 단기간 눈앞의 기능에만 집중하다가 코드의 품질이 저해되는 방식으로 개발이 진행되어 언젠가는 다시 수정해야 하는 일거리가 남겨지는 것을 ‘기술 부채(Technical Debt)’라고 부른다. 스켈터랩스의 코드 리뷰 문화는 사소한 영역까지 기술 부채를 남기지 않는다. 궁극적으로는 짧은 기간 완성도 높은 프로그램을 만들 수 있게 해주는 문화다. 엄격한 코드 리뷰가 가능한 것은 스켈터랩스의 개발자 역량이 높기 때문이기도 하다. 개발자들이 모두 기술에 대한 근본적인 이해와 최신 기술에 대한 섭렵을 두루 갖추었기에 타인이 작성한 코드도 바로 이해할 수 있다. 수준 높은 동료와 함께 일하며 피드백 받고 성장할 수 있다는 것은 회사의 굉장한 강점이라고 생각한다.사진2. 규홍 님과 다른 팀원 간의 코드 리뷰 모습.Q. 코드 리뷰 문화가 유익하기도 하지만, 일종의 압박감도 있을 것 같다. A. 압박감으로 여겨본 적은 없다. 한국 사회에서 개발자의 커리어에 대한 얘기를 나누다 보면 자연스럽게 ‘회사 일을 하다 보니 공부할 시간이 없어서 최신 기술을 알지 못해 뒤처진다.'라는 볼멘소리가 나온다. 그러나 스켈터랩스에서는 개발자 모두가 엄격한 코드리뷰를 거치는 과정에서 자연스럽게 더 나은 성능의 코드, 동료가 더 잘 이해할 수 있는 코드, 예상치 못한 예외 상황을 고려하는 코드를 작성하는 법을 실시간으로 배우게 되고, 때로는 그 과정에서 자연스럽게 코드 리뷰자가 제안하는 최신 기술에 대해 공부하고 습득하며 실력을 늘려나간다. 덕분에 코드 리뷰를 마치고 나면, 다음에 어떻게 해야 개선된 코드를 짤 수 있을지에 더 집중할 수 있고 실제로도 더 나은 코드를 작성할 수 있게 된다.물론 이런 문화가 신규 입사자로서는 다소 답답할 수 있을 것 같다. 나 또한 초반에는 ‘굳이 이런 디테일까지 다 잡아가며 이렇게 리뷰를 남겨야 할까'라는 생각을 해본 적도 있다. 그러나 스켈터랩스와 함께하는 시간이 점점 길어질수록, 꼼꼼한 리뷰로 기술 부채를 최소화하는 것이 팀 전체에도, 나의 성장에도 도움이 된다는 걸 느낀다.Q. 아무리 뛰어난 개발자가 있더라도 코드를 작성하는 사람은 한 명인데, 이를 함께 리뷰하다보면 작성된 코드를 이해하지 못하는 경우가 발생하지는 않나.A. 물론 그럴 수 있다. 때문에 스켈터랩스에서는 코드의 공동 소유, 공동 이해 개념을 깊이 이해하고, 잘 지킬 수 있게 만든다. 나만 이해할 수 있는 코드를 작성하면 장기적으로 다른 개발자들의 수정과 응용이 어려워진다. 그래서 스켈터랩스에서는 각 프로그래밍 언어별로 코딩 스타일 가이드를 준수할 것을 권장하고, 코드 리뷰 이전에도 가이드 준수 여부를 점검하는 도구를 활용하고 있다.Q. 스켈터랩스를 자랑한다면.A. 스켈터랩스는 아직 성장 중인, 그래서 ‘함께 만들어 갈 여지가 많은 회사'다. 나는 개인적으로 대기업부터 창업 초창기 단계의 스타트업까지 다양한 회사를 경험했는데, 이러한 과정에서 구성원 한 명 한 명이 회사의 문화와 기술적 원칙을 만들어가는데 얼마나 큰 영향을 주는지를 느꼈다. 스켈터랩스는 다양한 배경을 가진 개발자와 서로 영감을 주고받으며 함께 성장해가는 곳이다. 개발자 직군의 동료들과 비개발자 직군의 동료들이 끊임없이 소통하며 시행착오와 함께 점점 더 나은 기업문화를 만들어가고 있다. 그리고 실제로 이런 문화가 완성도 높은 프로그램을 만드는 데에 긍정적인 기여를 하고 있고, 현재는 성공 경험을 조금씩 안겨주고 있는 단계다. 역량 있는 인재들과 최신의 기술을 활용하여 새로운 결과물을 창출하는 것에 관심 있는 이들이라면 입사를 추천하고 싶다.#스켈터랩스 #사무실풍경 #업무환경 #사내복지 #기업문화 #개발팀 #팀원인터뷰 #팀원소개 #팀원자랑
조회수 2894

iOS 아키텍처 패턴(MVC, MVVM, VIPER)

Overview“글 한 번 써보실래요?” 입사하고 일주일이 지나 기술 블로그에 글을 써 보라는 제안을 받았습니다. 여러 고민 끝에, 아이폰 앱(이하 ‘iOS’) 주니어 개발자로서 프로젝트 경험과, 공부한 내용을 바탕으로 글을 쓰기로 했습니다. 적절한 짤이라고 생각하는 중iOS 개발자 사전 준비iOS 개발자의 길에 들어섰다면 이미 앱 개발과 개발 언어에 대해서는 알고 있을 겁니다. 개발 프로그램 Xcode와 프로그램을 지원하는 macOS 환경, 개발 언어 Swift 또는 Objective-C, iOS 앱 프로그래밍 등 iOS 앱을 개발하기 위해 필요한 내용까지도요. 우선 ‘iOS 주니어 개발자라면 꼭 알고 있어야 할 것’들을 아래 목록과 같이 정리했습니다. 글을 읽기 전, 목록 중에서 공부가 더 필요한 것이 있다면 꼭! 검색해보세요. Xcode, macOSApple Developer ProgramSwift or Objective-CCocoa TouchUIKitAuto Layout…iOS Architecture Patterns(아키텍처 패턴)“Viper 패턴 들어보셨어요?” Viper는 단순히 ‘독사’를 의미하는 줄 알았는데, MVC 패턴와 같이 디자인 패턴의 한 종류라는 건 입사하고 나서 알게 됐습니다. MVC와 SingleTon(싱글톤) 패턴은 익숙했지만 Viper 패턴은 생소했습니다. Viper 패턴을 3일 안에 분석하겠다는 저의 부끄러운 과거를 반성합니다... ㅜㅜ검색해보니 다양한 디자인 패턴을 찾을 수 있었습니다. iOS 개발자는 앱 프로젝트를 시작하기 전 또는 이미 진행되고 있는 프로젝트에서 개발을 시작한다면 우선 어떤 패턴으로 설계되어 있는지 파악해야 합니다. 그러므로 오늘은 iOS 개발에 주로 사용되는 패턴인 MVC, MVVM, VIPER를 간단하게 살펴보겠습니다.MVCMVC 패턴Model(모델), View(뷰), Controller(컨트롤러). Model에서는 애플리케이션에서 사용할 데이터들을 관리하고, View는 유저 인터페이스를 표현 및 관리합니다. Controller는 View와 Model의 다리 역할을 해 View의 입력을 Model이 반영하고, Model의 변화를 View에 갱신하는 역할을 합니다. 하지만, 애플의 MVC 패턴은 기존 MVC 패턴과 다릅니다. View와 Controller가 강하게 연결되어 있어 View Controller가 거의 모든 일을 합니다.1) 애플 MVC 패턴MVVMMVVM 패턴Model(모델), View(뷰), ViewModel(뷰모델). Controller를 빼고 ViewModel을 추가한 패턴입니다. 여기서 View Controller가 View가 되고, ViewModel이 중간 역할을 합니다. View와 ViewModel 사이에 Binding(바인딩-연결고리)가 있습니다. ViewModel은 Model에 변화를 주고, ViewModel을 업데이트하는데 이 바인딩으로 인해 View도 업데이트됩니다. ViewModel은 View에 대해 아무것도 모르기 때문에 테스트가 쉽고 바인딩으로 인해 코드 양이 많이 줄어듭니다.import Foundation // ViewModel var gameScore: Int? var gameScoreLabel: UILabel func updateGameScoreLabel() {   var text = ""   if let gameScore = gameScore, gameScore == 100 {       text = "Excellent!!"   } else if let gameScore = gameScore, gameScore >= 90 && gameScore < 100>       text = "Great Job!"   } else if let gameScore = gameScore, gameScore < 90>       text = "Not Bad~"   }   gameScoreLabel.text = text } // View Controller gameScoreLabel.text = viewModel.updateGameScoreLabel간단한 예를 들면, 게임 점수에 따라서 textView에 보여줄 내용을 담당하는 함수 등, View에서 변화가 일어나는 함수들이 View Controller에 정의되어 사용하는 경우가 많을 겁니다. 이런 함수들이 점점 많아지면 View Controller가 Massive, 많은 코드를 담게 됩니다. 그래서 이런 함수들을 ViewModel에 옮기고, 값들을 미리 세팅한 다음에 view controller에서 viewModel을 선언하고 viewModel의 함수를 불러오는 식으로 사용하면 됩니다. 매우 간단한 예제이기 때문에 대략 viewModel과 view controller에서 어떻게 사용하는지만 보시면 될 것 같습니다. 이 패턴은 주로 Reactive programming(ReactiveCocoa, RxSwift 등)을 할 때 많이 사용하는 패턴이어서 다음에 설명하겠습니다.VIPERVIPER 패턴View(뷰), Interactor(인터렉터), Presenter(프리젠터), Entities(엔티티), Router(라우터). MV(X) 패턴과 다른 패턴으로 MVC 패턴을 대체하기 위해 만들어진 패턴입니다. 먼저 Entity는 그저 모델 객체입니다. 단순하게 어떤 모델의 속성들만 있는, Dumb Model이라고 부를 수 있습니다. 이 모델 객체를 조작하는 것이 바로 Interactor입니다. 어떤 행동(behavior or use case)에 따라서 모델 객체를 조작하는 로직이 담겨 있습니다. 작업이 완료되어도 View에 아무런 영향 없이 오로지 데이터 작업만 합니다.Presenter는 데이터를 Interactor에서 가져오고, 언제 View에 보여줄지 결정합니다. View에 보여주기 전 내용을 준비하는 로직을 담당한다고 생각하면 됩니다. View는 Presenter에서 어떻게 보여줘야 할지 요청대로 디스플레이하고, 사용자의 입력을 받으면 다시 Presenter로 넘깁니다. Presenter는 View/ViewController, Interactor, Router와 상호작용합니다. Interactor로부터 조작된 데이터를 가져오고, 디스플레이하기 위해 데이터들을 준비한 다음 View/ViewController에 보냅니다.Router 또는 Wireframe은 화면 전환(navigation information)을 담당합니다. Presenter가 “언제” 화면을 전환해야 하는지 안다면, Router는 화면 전환을 “어떻게” 하는지 알고 있습니다. Router는 화면 전환 애니메이션을 구현하고, View Controller를 생성하여 Presenter와 연결합니다.항목내용ViewPresenter의 요청대로 디스플레이하고, 사용자 입력을 Presenter로 보내는 작업을 합니다.InteractorUse case에 따라서 Entity 모델 객체를 조작하는 로직을 담고 있습니다.PresenterInteractor로부터 데이터를 가져오고, View로 보내기 위해 데이터를 준비하여 “언제” View에 보여줄지를 결정합니다.Entity모델 객체. Dumb Model.Router(Wireframe)화면 전환(navigation information)을 담당하며, Presenter가 “언제” 화면 전환해야하는지를 안다면, Wireframe은 화면 전환을 “어떻게” 하는지를 알고 있습니다.하...지금까지 설명한 내용들은 막상 프로젝트 만들어 소스를 작성하려고 하면 막막해집니다. 역할이 잘 분할되어 있기에 앱의 기능을 하나 정하여 interactor, entity, presenter, view, router 만들고, 또 앱의 기능에 따라서 다시 interactor, entity,…. 고민을 많이 해야 해서 다시 MVC 패턴으로 돌아가고 싶은 마음이 생깁니다.크게 보면 Add Module와 List Module, 그리고 공통적인 모델(데이터)을 잘 분리한 앱 구조Conclusion도대체 우리는 왜 다양한 앱 디자인 패턴을 알아야 할까요? 그 이유는 바로 앱의 특성에 따라 적합한 설계를 가지고 작업해야 하기 때문입니다. 간단한 앱 프로젝트는 쉽게 개발하고 적용할 수 있는 MVC 패턴이 더 적합합니다. 반대로 MVVM 패턴이나 VIPER 패턴을 적용하면 점점 커지는 앱 프로젝트에 잘 대응할 수 있습니다. 또는 어떤 디자인 패턴이 적용된 앱 프로젝트에 참여하면, 그 디자인 패턴에 대해 알아야 앱 구조를 이해하고 기능을 추가하거나 수정할 수 있고, 작업하는 시간을 줄일 수 있을 겁니다.가장 좋은 패턴은 사람마다 차이가 있습니다. 패턴마다 장단점도 있습니다. 다만 어떤 패턴이든지 간에 구조화되고 정리된 코드는 쉽고, 직관적입니다. 이 글 하나만으로 앱 패턴을 완벽하게 마스터할 수는 없어도 패턴의 종류와 특징을 알게 되었다면 본전입니다. 다음 편도 기대해주세요! :-) 도움말 1) View Controller에서는 Controller가 View의 life cycle(라이프 사이클)에 관여하기 때문에 View와 Controller를 분리하기 어렵습니다. 개발자들 사이에서는 Massive View Controllers라고도 불립니다. 앱을 테스트할 때, Model은 따로 분리되어 테스트를 할 수 있어도 View와 Controller는 강하게 연결되어 있기 때문에 각각 테스트하기 어렵습니다. 참고문헌 iOS Architecture Patterns: Demystifying MVC, MVP, MVVM and VIPER글김주희 사원 | R&D 개발1팀[email protected]브랜디, 오직 예쁜 옷만#브랜디 #개발문화 #개발팀 #업무환경 #인사이트 #경험공유 #iOS
조회수 1228

에이스프로젝트 추천도서 - 프론트 편

안녕하세요!기업 문화가 좋은 야구게임 개발사에이스프로젝트입니다.기획팀 편에 이어 2탄!에이스프로젝트의 대소사(?)를 책임지는 '프론트'편을 준비했습니다!프론트는 조직문화 담당자부터 인디자이너까지 다양한 인재들로 구성되어 있어요.하는 일이 다양한 만큼 추천도서의 스펙트럼도 넓었는데 그중 다섯 권을 엄선했다고 합니다.에이스프로젝트 프론트가 추천하는한 번쯤은 읽어보면 좋은 추천 도서 Best 5!1. 구글의 아침은 자유가 시작된다 - 라즐로 복[ 이미지 출처 : 예스 24 ]자유롭게 일하는데 성과도 좋은 조직문화, 구글은 어떻게 만들었을까조직문화 담당자들에게 생각할 주제를 던져주는 책2. 배민다움 - 홍성태[ 이미지 출처 : 예스 24 ]회사에 맞는 문화를 만드는 과정에 대한 정리가 잘 되어 있는 책3. 내 문장이 그렇게 이상한가요? - 김정선[ 이미지 출처 : 예스 24 ]칼럼 쓸 때 도움이 많이 됐던 글쓰기 실용서교정교열 경력 20년이 넘었다는 작가분의 내공이 느껴지는 책4. 좋은 문서 디자인 기본 원리 29 - 김은영[ 이미지 출처 : 예스 24 ]"자네는 디자이너도 아닌데 어떻게 이렇게 전달력이 좋나!"좋은 내용을 더 좋게 만들어 주는 문서 디자인 기본서5. 디자이너 사용설명서 - 박창선[ 이미지 출처 : 예스 24 ]프론트 인디자이너의 추천서!디자이너와의 원활한 협업을 원하는 모든 사람들에게 이 책을 추천합니다프론트는 인사, 채용, 회계, 홍보 등 각자의 전문 영역이 있지만 결국은 다 함께 좋은 회사를 만들기 위해 노력하는 팀입니다. 위 다섯 개의 도서는 프론트가 공통적으로 읽고 추천한 도서라고 해요 :-) 이상 "각자, 그리고 함께 조직문화를 만들어가는" 프론트의 추천도서였습니다!다음은 '그래픽팀'의 추천도서로 찾아올게요 ;)
조회수 2905

챗봇과 인공지능 머신러닝 ㅡ Part 1/2

스타워즈를 보신 분이라면 거기에 나오는 난쟁이 로봇 R2D2와 키다리 로봇 C3P0를 아실 것이다. 친근한 R2D2는 전자음을 조정해 인간과 대화를 하며 주로 말 잘하고 박식한 로봇인 C3P0가 통역을 해준다.이런 충실하면서 똑똑한 친구들이 옆에서 항상 나를 도와준다면 어떨까? 정말 좋을 것이다. 만약 매일 보는 스마트폰 안에서도 나의 질문에 답해주는 이런 고마운 친구들이 있다면 얼마나 좋을까? 이런 저런 생각을 하다보면 우리는 대화형 로봇의 필요성을 느낀다.챗봇(Chatbot)이란?챗봇의 정의는 “대화형 인터페이스 상에서 규칙 또는 지능으로 유저와 소통하는 서비스”이다. 이 말을 하나하나 풀어보자.먼저, 대화형 인터페이스란 뭐지? 어렵다. 쉽게 설명해 보자. 인터페이스는 사람과 컴퓨터를 연결하는 장치라고 한다. 역시 어렵다. 아! 그냥 스마트폰 앱으로 보면 된다. 그럼 소통한다는 말은 대화한다는 것이므로 스마트폰 앱에서 일방향이 아닌 양방향이 가능하다는 얘기다. 어! 이상하다. 양방향이라면 나의 말에 응대하는 로봇은 뭐로 움직이는 거지? 궁금하다. 누가 일정한 규칙으로 만들어 논건지 아니면 우리처럼 지능이 있는 건지. 지능이 있다면 그런 지능은 뭐지? 점차 우리는 자연스럽게 인공지능에 다가간다.인공지능(Artificial Intelligence)이라는 용어는 1956년 미국 다트머스의 한 학회에서 존 매카시가 처음 사용했다고 한다. 원래 인공지능은 소프트웨어인 정신을 말하고 로봇은 하드웨어인 육체를 말하는 것이지만 정신없이 육체가 존재할 수 없는 것처럼 로봇을 얘기하면 당연히 인공지능은 따라간다.학자들은 인공지능을 강(强)인공지능과 약(弱)인공지능으로 구분한다. 간단히 얘기하면 강인공지능이란 자의식이 있는 인간에 가까운 지능이고 약인공지능은 자의식이 없다. 자아가 없으며, 명령받은 일만을 수행한다. IBM의 왓슨(Watson), 작년에 인공지능의 붐을 가져온 구글의 알파고(Alpha-GO) 등은 모두 약인공지능이다. 이런 인공지능을 구현하는 기술은 무엇인가? 바로 기계한테 학습을 시키는 머신러닝(Machine Learning)이다.1959년 아서 사무엘은 머신러닝을 "기계가 일일이 코드로 명시하지 않은 동작을 데이터로 부터 학습하여 실행할 수 있도록 하는 알고리즘을 개발하는 연구 분야"라고 정의했다. 여기서 학습이란, 입력 값을 받아 결과 값을 내는 모델을 만드는 표현과 표현을 통해 주어진 업무가 얼마나 잘 수행됐는지 알아보는 평가, 그리고 평가에서 설정한 기준을 찾는 최적화로 구성된 일련의 과정을 말한다. 중요한건 우리가 시키지 않은 일도 학습에 의해 자율적으로 처리한다는 것이다. 정말 신기하지 않은가?이제 챗봇이 뭔지 감이 잡힌다. 스마트폰 앱상에 존재하는 로봇인데, 물론 육체는 화면의 아이콘으로 밖엔 안보이지만 인공지능을 가지고 머신러닝에 의해 동작을 하면서 우리와 대화를 하는 그분. 그렇다면 이제 남은 건 이분의 지능이 어느 정도인지 또 얼마나 일을 잘하는 지로 판가름 난다.우리는 평생 공부를 한다. 이제는 학교를 졸업하고 나서도 항상 배워야 한다. 학습이 없다면 지능도 없다. 학습은 일일이 지도받는 지도학습과 알아서 공부하는 자율학습이 있다. 알아서 공부하려면 먼저 머리에 지식이 많아야 한다. 역시 기계도 사람과 비슷하게 배운다.  다음시간엔 챗봇에게 학습을 시켜 지능을 가지게 하는 방법에 대해 알아본다.> Part 2에서 계속
조회수 2541

Next.js 튜토리얼 8편: 컴포넌트 스타일링

* 이 글은 Next.js의 공식 튜토리얼을 번역한 글입니다.** 오역 및 오탈자가 있을 수 있습니다. 발견하시면 제보해주세요!목차1편: 시작하기 2편: 페이지 이동 3편: 공유 컴포넌트4편: 동적 페이지 5편: 라우트 마스킹6편: 서버 사이드 7편: 데이터 가져오기 8편: 컴포넌트 스타일링 - 현재 글9편: 배포하기개요지금까지 컴포넌트를 스타일링 하는 것을 미뤄왔습니다. 그러나 이제는 몇 가지 스타일을 적용해볼만 합니다.React 애플리케이션에는 컴포넌트를 스타일링 할 수 있는 여러가지 기술들이 있습니다. 크게 두 가지 방법으로 분류할 수 있습니다:1. 전통적인 CSS 파일 기반의 스타일링 (SASS, PostCSS 등)2. CSS in Js 스타일링 결과적으로 전통적인 CSS 파일 기반의 스타일링(특히 SSR)은 실용적인 문제가 많아 Next.js에서 스타일을 지정할 때는 이 방법을 사용하지 않는 것이 좋습니다. 대신 CSS in JS 방법을 추천합니다. 이 방법은 CSS 파일들을 불러오는 것보다 개별적인 컴포넌트 스타일링 할 때 사용 할 수 있습니다.Next.js는 styled-jsx라는 CSS in JS 프레임워크를 미리 설치해두었습니다. 컴포넌트에 이미 익숙한 CSS를 작성할 수 있습니다. 이 CSS는 해당 컴포넌트에만 적용되며 심지어 하위 컴포넌트에도 적용되지 않습니다.이는 CSS가 범위가 있음을 뜻합니다.styled-jsx를 어떻게 사용할 수 있는지 살펴봅시다.설치이번 장에서는 간단한 Next.js 애플리케이션이 필요합니다. 다음의 샘플 애플리케이션을 다운받아주세요:아래의 명령어로 실행시킬 수 있습니다:이제 http://localhost:3000로 이동하여 애플리케이션에 접근할 수 있습니다.home 페이지 스타일링하기home 페이지(pages/index.js)에 스타일을 추가해봅시다.간단히 pages/index.js를 다음과 같이 변경해주세요:   <style jsx> 엘리먼트를 살펴봅시다. 이것은 CSS를 작성하는 곳입니다.코드를 바꾼 후 블로그 home 페이지는 다음과 같이 보일 것입니다:위의 코드에서 스타일 태그 안에 직접 스타일을 작성하지 않고 템플릿 문자열 안에 작성하였습니다.템플릿 문자열({``}) 없이 직접 CSS를 작성해봅시다:어떤 일이 일어날까요?- 아무 일도 일어나지 않는다.- 새로운 스타일이 적용된다.- "문법 에러: 기대되지 않는 토큰"이라는 에러가 발생한다.- "허용되지 않는 스타일 제공자"라는 에러가 발생한다.스타일은 템플릿 문자열 안에 위치해야 합니다styled-jsx는 babel 플러그인을 통해 동작합니다. babel 플러그인은 빌드 과정에서 모든 CSS를 분해하고 적용합니다. (스타일이 추가 시간 없이 적용됩니다)styled-jsx 내에 제약 조건을 제공합니다. 나중에 styled-jsx 안에 동적 변수를 사용할 수 있습니다. 이것이 스타일을 템플릿 문자열 ({``}) 안에 작성해야하는 이유입니다.스타일과 중첩된 컴포넌트home 페이지에 작은 변화를 만들어봅시다. 다음과 같이 링크 컴포넌트를 분리시켰습니다:    import Layout from '../components/MyLayout.js'   pages/index.js 안의 내용을 위와 같이 수정해봅시다.무슨 일이 일어나나요?- 아무런 일도 일어나지 않는다.- 링크가 아닌 h1만 스타일이 적용된다.- 페이지에 에러가 발생한다.- 콘솔에 에러가 발생한다.중첩된 컴포넌트에는 적용되지 않습니다위의 코드를 실행하면 다음과 같이 보입니다:보다시피 CSS는 하위 컴포넌트 내부의 엘리멘트에는 적용되지 않습니다.styled-jsx의 특징은 더 큰 애플리케이션에서 스타일들을 관리할 때 도움이 됩니다.이 경우에는 하위 컴포넌트에 직접 스타일을 적용해야 합니다. 지금 상황에서는 링크 컴포넌트에 직접 스타일을 적용해야 합니다:다른 방법로는 global selectors을 사용할 수 있습니다.전역 스타일때때로 하위 컴포넌트 안의 스타일을 바꿔야 합니다. 일례로 React에서 마크다운을 사용하는 경우가 있습니다. post 페이지(pages/post.js)에서 볼 수 있습니다.post 페이지는 전역 스타일이 유용하게 쓰일 수 있는 곳입니다. styled-jsx를 사용하여 몇 가지 전역 스타일을 추가해봅시다. pages/post.js에 다음과 같은 내용을 적용해주세요.다음 내용을 적용하기 전에 npm install --save react-markdown 명령어를 통해 react-markdown 컴포넌트를 설치해주세요. 무슨 일이 일어나나요?- 아무런 일도 일어나지 않는다.- 마크다운 컨텐츠에 스타일이 적용된다.- 페이지에 에러가 발생한다.- 콘솔에 에러가 발생한다.전역 스타일이 동작합니다전역적으로 스타일이 적용되므로 잘 동작합니다.이 기능은 매우 유용할 수 있지만 항상 전역 prop 없이 스타일을 작성하길 추천합니다.여전히 일반적인 스타일 태그보다 좋은 방법입니다. styled-jsx를 사용하면 필요한 모든 접두사와 CSS 유효성 검사가 babel 플러그인 내부에서 수행되어 추가적인 런타임 오버헤드가 없습니다.다음엔 무엇을 해야할까요이 편에서는 styled-jsx의 표면만 다루었습니다. 더 많은 것들을 할 수 있습니다. styled-jsx Github 저장소에서 더 많은 내용을 참고하세요.Next.js에서 꽤나 괜찮은 다른 스타일링 방법들이 있습니다. 이 부분도 같이 참고해주세요.#트레바리 #개발자 #안드로이드 #앱개발 #Next.js #백엔드 #인사이트 #경험공유
조회수 1418

독서모임 스타트업에 개발자나 디자이너가 필요한가요?

트레바리는 독서모임을 운영하는 회사다. 멤버들이 책을 읽고, 독후감을 쓰고, 아지트에서 여러 사람들과 다양한 대화를 나눌 수 있도록 만들어준다. 아날로그적이려면 한없이 아날로그 할 수 있는 회사가 바로 트레바리다. 그러다 보니 트레바리의 첫 빌트인(?) 개발자 겸 디자이너인 나는 가끔 이런 질문을 받기도 한다. "트레바리에 개발자나 디자이너가 필요한가요?" 작년 11월과 12월, 개발과 디자인을 총동원해서 멤버십 신청 페이지의 UI/UX 개선 작업을 진행했다. 원래의 홈페이지보다 편하게 신청하도록 토스 결제를 연동하는 등 프로세스를 재편하였고, 판매할 프로덕트가 의도대로 보이도록 레이아웃을 다시 구성하였다. 컨텐츠의 가독성을 위해 컴포넌트들의 디자인도 깔끔하게 변경했다. 개선된 프로세스와 인터페이스라면 멤버십에 신청하는 사람들이 늘어날 거라고 확신했다. 홈페이지를 방문만 하고 멤버십에 신청하지 않은 이유는 '홈페이지가 불편하고 안 예뻐서'라고 생각했기 때문이었다.결론부터 말하자면 내 가설은 완전히 틀렸다. 개선된 홈페이지를 런칭했지만 방문 유저 대비 신청한 유저의 비율에는 큰 변화가 없었다. 다급히 주변에 조언을 구하기 시작했고 마켓컬리의 이지훈 님이 해주신 조언이 한참을 머릿속에 멤돌았다. "트레바리는 오프라인 경험이 메인이므로 홈페이지의 변화가 큰 효과가 없을 수 있음을 인정하고 시작해야 해요. 홈페이지는 광고를 보고 온 유저들이 독서모임에 가기 전까지 거쳐 가는 곳이에요."그렇다. 트레바리 홈페이지는 오프라인 독서모임에 참여하기 위한 건널목일 뿐이였다. 건널목이 아무리 좋다 한들 목적지가 탐탁지 않으면 사람들이 건너가지 않을 것이었다. 마찬가지로 홈페이지가 아무리 편하고 예뻐도 아지트에 와서 나누는 대화가 무의미하고 재미없다면 사람들이 트레바리를 찾지 않을 것이다.덕분에 트레바리 특성상 홈페이지를 위한 개발자나 디자이너 크루(=직원)가 필요한지 자문하게 되었다. 건널목 역할을 수행하는 홈페이지가 필요한 것이라면 이미 충분하다고 생각했다. 추가로 필요한 기능이 있다면 그때그때 적당한 프리랜서를 고용하는 게 합리적일 수도 있었다. 그렇다면 맨 위의 질문에 대한 대답은 "아니요. 필요 없어요. 프리랜서면 충분해요."가 되는 것이었다.내가 크루로서 잘 쓰일 수 있는 일은 무엇일까?얼핏 생각하기에 프리랜서면 충분해 보이지만 분명 내가 크루로서 잘 쓰일 수 있는 일이 있을 거라 생각했다. 그리고 그것을 오프라인 트레바리와 온라인 트레바리 사이에 간극이 있다는 점에서 찾았다.오프라인 트레바리는 꽤나 매력적이다. 한 시즌을 경험한 두 명의 멤버 중 한 명은 다음 시즌에도 멤버십을 신청한다. 물론 나머지 한 명까지 신청하게 만들게끔 개선할 부분들이 남아있지만 그래도 60%가 넘는 리텐션은 트레바리가 다시 올 만한 서비스라고 말해준다.온라인 트레바리는 사정이 다르다. 많은 사람이 방문하지만 금세 나가버린다. 지금의 트레바리 홈페이지는 트레바리가 뭐 하는 곳인지, 트레바리를 하면 어떤 사람이 될 수 있는지, 트레바리에서는 어떤 사람들을 만날 수 있는지 잘 알려주고 있지 않다. 미리 지인이나 미디어를 통해 트레바리의 매력을 알고 온 사람들만이 홈페이지를 샅샅이 뒤져본 후에나 어떤 곳인지를 엿볼 수 있다.이 불협화음을 잘 조율하는 일을 내가 잘 할 수 있겠다는 생각이 들었다. 나는 원래 작년 초까지 멤버였다가 트레바리 매력에 빠져 입사까지 하게 된 진성 유저였다. 덕분에 트레바리가 얼마나 좋은지, 어떻게 트레바리를 통해 예전보다 멋진 사람이 될 수 있는지, 트레바리에서 얼마나 좋은 사람들을 만날 수 있는지를 알고 있었다. 그리고 이것들을 동네방네 열심히 소문을 내고 싶은 사람이었다.트레바리 홈페이지가 오프라인 트레바리에 오기 위한 건널목이라면 건널목 입구에 삐까뻔쩍한 간판도 크게 달고, 안내판도 만들어 건널목 너머에 얼마나 멋진 곳이 있는지 넘어오고 싶게끔 기대감을 심어주고 싶다. 우리의 비전인 '세상을 더 지적으로 사람들을 더 친하게'처럼 내가 트레바리에 온다면 더 지적이고 멋진 사람이 될 수 있고, 사람들과 더 친하게 지낼 수 있음을 잘 설명해주고 싶었다. 사람은 자기가 정말 좋아하는 것을 설명할 때 지치지 않고 그 어느때보다 열심히 목소리를 높일 수 있다고 생각한다.물론 나 혼자서는 힘들 것이다. 그래서 다른 크루들과 같이 어떻게 잘 전달할 수 있을지 치열하게 고민해보기로 했다. 그 일례로 최근에 영훈님과 같이 사내 스터디를 시작했다. 이런 점들이 단순히 시키는 일만 해내는 프리랜서보다 훨씬 더 잘 쓰일 수 있는 크루로 만들어 줄 것이라고 믿는다. 아직 '그래서 구체적으로 어떻게?'까지는 고민을 끝마치지 못했지만, 드디어 어떤 방향으로 무슨 역할을 하는 사람인지를 결정하였다. 이 결정을 시작으로 올해는 '회사에 더 많은 기여를 할 수 있는 크루가 될 수 있지 않을까'하는 설렘과 '그러려면 훨씬 더 잘해야겠다'는 부담이 가득한 채로 일 년을 맞이하게 되었다.올 한 해 '세상을 더 지적으로 사람들을 더 친하게' 만들어줄 우리 크루들!#트레바리 #기업문화 #조직문화 #스타트업 #인사이트
조회수 1671

금융 테크놀로지와 #개발

여름은 언제 끝날까? 주말부터 더위에 지친 오늘, 단비 같은 시원한 소식이 핀다에 찾아왔다.한국형 핀테크 세계 금융 판도 흔든다`제1회 매경 핀테크 어워드` 11기업 선정핀다 장려상 수상!매일경제에서 주최한 Fintech Awards에서 #Finda 가 장려상을 수상했다는 소식. (감사합니다!) 한 주의 시작을 청량감 가득한 시원한 뉴스로 시작하다니 흥이 절로 난다.금융상품 비교 추천 플랫폼으로서 Finda 이외에도 온라인 가상화폐인 '비트코인(Bitcoin)'을 활용한 신개념 해외송금 서비스 업체 '센트비' 등 총 11개의 핀테크 기업이 선정되었다. 지금까지 걸어온 우리의 걸음마에 시원한 바람을 넣어주는 것 같아 핀다 가족들이 더 힘이 나는 날이다.나는 개발자이다.핀다에서 금융상품의 검색을 시작의 용이성을 시작으로 금융 테크놀로지에 기여하고자 하는 개발자이다. #핀테크스타트업 '핀다'와 함께한 나의 이야기를 해보고자 한다.  개발 경력 10년이 넘어버린 때. 지금으로부터 2-3년 전쯤일 게다. 한창 늘어져만가는 시점에서 같이 일하던 회사 이사님이 솔깃한 제안을 해왔다. #스타트업 #Startup! WHAT?경력으로도 가늠하겠지만, 적지 않는 나이이기도 했고, 오래전 말아먹은(?) 안 좋은 기억도 어렴풋이 남아있다. (무엇인지에 대해서는 구체적으로 적진 않겠다ㅎ) 그렇지만, 뭔가의 변화가 필요했던 시점에... 나의 귓가를 울리는 새로운 단어 Start up. 뭐랄까. 단비와도 같은? 오늘 매경에서 수상한 그런 '단비'보다 문자 그대로 '꼭 필요할 때 알맞게 내리는 비 = 단비' 같은 결정적인 모먼트.하여튼 그러했다.돌파구가 필요했던 시점. 적절했다.라고나 할까.2년을 좁디좁은 사무실에서 그야말로 쉼 없이 뒹굴었다. 그 사이 늦은 결혼에 낳은 늦둥이도 세상 빛을 보았고 세상은 더욱더 팍팍해졌으며 더불어 2년의 시간이 무색해져 버릴 만큼의 성적표가 떨어져 버린 거다.다시 새로운 무언가를 찾아야 했던 시절. 딱히 어떤 목표랄까 기대 같은 건 없이 가벼이 만났던 인연이 지금 내가 이 자리에 있게 된 운명 같은 것이었다고 말할 수 있겠다.스타트업 + 핀테크 개발자로 변신개발 13년 차에 다시 시작한 스타트업.게다가 그 핫하다는 핀테크 바닥이란 말이다.어찌할 바를 모르던 모바일 개발자 덕분에 한 달을 투여했던 API 개발은 모두 쓸모없는 일이 되어버렸다. 그나마 소득이라면 그 한 달의 기간 동안 같이 얘기하고 토론했던 Co-Founder 두 분과의 인연은 깊어졌다. 그 덕에 기대도 못했던 핀테크 업체의 개발 헤더 자리에 비비고 앉게 되어 버렸다. 물론 내 의지가 전혀 없었다고 얘기할 순 없겠지만 말이다. (사실은 매우 의욕적이었다.)하지만 개발일이라는 게 서로 얼굴 맞대며 일해도 어려운 것을, 한 달이 넘게 떨어져 있었으니 서로 일정을 맞추기도 어려웠고 서로의 상황이 달라 업무 상호 확인도 어려운지라 제대로 돌아가기가 힘들었다. 결국 모바일 버전의 프로토타입은 접어두고 "그래~! 웹 버전으로 시작하자"였다. 어차피 만들어둔 API도 있겠다, 프런트엔드만 올리면 되는 일.그리 시작한 "FINDA"의 웹서비스 개발은 드. 디. 어 지난 1월에 세상에 빛을 보게 되었다. 아직 조금 모자란 "Beta"라는 이름을 걸고 말이다. 눈물이 다 날 지경이었다. 론칭 며칠 전까지만 해도 할 수 있을까? 였는데.. 할 수 있게 되다니.빠른 시일 내에 베타 서비스로 완성을 해야 했으나, 마음에 차지 않는 부분들이 여럿 있을 수밖에 없었다. 최종적인 모습인 "개개인의 성향과 상황에 따른 맞춤 추천 서비스"를 지향하기 위해선 많은 부분들이 필요했던 것. 여러 상품들을 담아두고 싶은 마음에 여러 방면으로 두 대표님들이 뛰어다니던 차, 금감위에서 오픈 API를 제공하기로 한다는 소식이 들렸다. 오호~! 뭔가 될 법한 일에는 이리도 딱 맞는 기회가 주어지는구나.금감원 API를 통해 상품군의 다변화와 다루는 금융 상품들의 개수도 많이 늘렸다. 덕분에 손봐야 하고 신경 써야 하는 일들이 많이 늘긴 해야 했지만 무언가 서비스가 성장하고 있다는 느낌이라. 방문자 수도 꾸준히 늘어 갔고 심심치 않게 외부 피드백도 손에 쥐게 되었다. 그렇게 한두 달이 정신없이 지나가고...핀다 서비스 테크놀로지- 개발자의 시선으로정식 론칭! 대망의 4월, 이젠 정말 실전이다. 정식 서비스 론칭은 베타 서비스 론칭에 비해서 그나마 수월했다. 베타 서비스 론칭 때 이미 겪은 바도 있었으니 미리미리 준비해 둔터일 게 다. 그래도 서비스 론칭인데 수월했다고는 하나 정신없는 건 어쩔 수 없는 모양.정식 서비스를 론칭한 후 핀다팀은 서비스 전반에 대해 다시금 되돌아보는 시간을 갖기로 했다. 이른바 Finda Hackathon~! 각 팀별로 서비스에 대한 생각과 앞으로 나아가야 할 방향 그리고 준비해야 할 사항 등에 대해 열띤 토론이 이어졌다. 개발자로서도 꽤나 의미 깊은 시간이 아니었나 싶다. 솔직히 시간에 쫓겨 개발에 몰두하다 보면 전체적인 그림을 못 보고 지엽적인 문제에 치중하게 될 때가 많은데, 이렇게라도 시간을 내어 서비스 전반에 걸쳐 되돌아볼 수 있는 시간을 가진다는 게 여러 가지 면에서 좋은 방법인 듯싶다.론칭 후에도 할 일이 많다. 서비스를 키워나가야 하기 때문. 마케팅팀도 보강되었고 지속적으로 인력도 늘어갔다. 외부 업체와의 MOU도 점차 늘려 나갔고  그에 따라 서비스에 상품군과 기능들도 많이 늘어왔다. 개발팀의 업무량도 자동적으로 증가. 상품에 대한 소비자들의 직접적인 의견을 들을 수 있는, 그리고 그에 따라 1) 상품 선택에 도움이 되는 리뷰 기능의 확충, 2) 소비자들이 상품의 조회에 그치지 않고 선택한 상품의 가입을 보다 더 쉽게 이룰 수 있도록 3) 상품 조회에서부터 선택, 가입에 이르는 플로워를 다방면으로 테스트하고 개선시켜 나간다든가 하는 일들이 많아졌다. 게다가 4) CMS 등의 내부 시스템의 개발까지 그야말로 눈코 뜰 새 없는 시간의 연속이었다.#육아코딩 집에서도 눈코뜰새 없이 열일 중ㅎ https://www.instagram.com/leepublic/론칭 이후 4개월이 지난 지금.건방지게 느껴질 수도 있지만 당연히 발전적이다. 여전히 성장할 여지(Room to Grow)가 상당히 많다. 그간 상품 수도 많이 늘었고, 서비스의 개선도 지속적으로 이루어져, 실질적은 성과들도 조금씩은 나타나기 시작했다. Stay hungry! 아직도 부족함을 느끼는 건 나만의 욕심은 아닐 것이다. 금융 소비자의 정보 불균형을 해소하겠다던 가치와 신념에 있어 정말 새발의 피만큼의 진전을 이루었겠지만 말이다.그래도 서로 비교할 수 있고, 간단한 몇 가지 항목만으로도 쉽게 상황에 맞는 상품을 볼 수 있다는 것만으로도 많은 시간과 기회비용을 아낄 수 있는 방법을 제기할 수 있어서 다행이라고 생각하고 있다. 솔직히, 나 스스로도 은행 대출을 끼고 집을 구입했던 사람으로서 어디 가서 물어보기도 힘들고 일일이 은행 사이트들을 찾아다니며 비교하기도 힘든데, 진작에 이런 서비스가 있었더라면 몇 번이고 써봤을 거다. 이건 진심이다.아직 해야 할 일은 많이 남아 있다. 처음부터 세세한 부분까지 모두 파악하는 건 어렵겠지만 개개인의 재정상태, 소비형태, 삶의 방식 등의 여러 가지 데이터를 기반으로 대출 및 예적금, 나아가 향유할 수 있는 금융생활에 대한 조언자, 설계자가 되고 싶고 또 그렇게 만들어갈 생각이다. 십원짜리 하나 쓰는 것도 잔소리할 테세다.사람을 기반으로 한 금융 테크놀로지를 꿈꾸며...그러기 위해선 "사람"에 대한 고민이 제일 필요한 일일 게다. 빅데이터라든가, 대용량 처리 시스템이라든가, 클라우드 서비스라든가, 금융 데이터 분석을 위한 Pandas나 데이터의 연관 관계 분석을 위한 딥러닝이라든가. 기술적인 부분들도 매우 중요하고 또 이루어져야 할 일이기도 하지만 그 무엇보다 중요한 건 역시 "사람"이 아닐까 싶다.DVD대여 회사로 출발하여 이제 글로벌 컨텐츠 공룡으로 인정받는 넷플릭스(Netflix) 성공의 기반은 기술도 아니고 콘텐츠도 아니었다. 바로 "사람"에 집중했던 것. 넷플릭스는 #하우스오브카드 (House of Card) 드라마를 출시하면서 “우리는 시청자들이 무엇을 보고 싶어 하는지 잘 알고 있으며, 분석 알고리즘을 통해 누가 케빈 스페이스 혹은 정치 드라마를 좋아하는지 파악하여 그들에게 추천할 것이다”라고 자신 한 바 있다.모든 데이터의 중심에는 "사람"이 있었다. "사람"에 대한 이해 위에 기술을 기반으로 콘텐츠를 입혀 개개인에게 보다 사람답게 다가갔던 게 성공의 열쇠가 아니었나 싶다.핀다 또한 그러한 길을 걸어가야 할 터,나 또한 사람을 기반으로 한 기술의 발전을 꿈꿔볼 일이다.핀다의 금융 테크놀로지이혁 드림Hyek from FindaHead of Engineer#핀다 #개발팀 #개발자 #팀원소개 #조직문화
조회수 4552

오픈소스 라이브러리를 사용해보자, CocoaPods! (KOR)

Overview개발 도중 내용이 복잡하거나 소스가 길면 종종 오픈소스 라이브러리를 사용합니다. 쉽게 원하는 기능을 구현할 수 있기 때문이죠. 그렇다면 오픈소스 라이브러리는 어떻게 앱에 가져와서 사용할까요? 바로 ‘CocoaPods(이하 코코아팟)’을 쓰면 됩니다.What is CocoaPods?코코아팟의 공식 웹사이트에서는 코코아팟을 이렇게 소개하고 있습니다.“CocoaPods is a dependency manager for Swift and Objective-C Cocoa projects”“코코아팟은 스위프트와 오브젝티브-C 코코아 프로젝트를 위한 의존성 매니저(dependency manager)다.”즉, ‘개발자가 편리하게 사용할 수 있게 오픈소스 라이브러리를 프로젝트와 연결해주는 환경 또는 도구’를 말합니다. 이로 인해 다양한 장점을 가지고 있는데요. 우선 코코아팟은 개발자가 개발한 앱에 라이브러리를 추가, 삭제, 업데이트 등의 관리를 할 수 있습니다. 예를 들어, 네트워크 관련 라이브러리를 개발자가 직접 개발하지 않고, Alamofire 라이브러리를 코코아팟으로 앱에 연결해 사용하는 것입니다. 둘째, 라이브러리 버전을 직접 지정하여 사용할 수 있어 업데이트 버전이 나와도 지정한 버전을 계속 사용할 수 있다는 점입니다. 만약 새로운 버전에 맞춰 개발할 준비가 되면 그때 업데이트를 하면 됩니다.CocoaPods에서 facebook을 검색하면 관련된 다양한 라이브러리가 나옵니다.How to use Cocoapods?1.코코아팟 설치하기개발한 앱에 사용할 오픈소스 라이브러리를 찾았다면 코코아팟을 설치해 앱과 연결해봅시다. 먼저 코코아팟을 설치하고 터미널 프로그램을 열어 아래와 같은 명령어를 입력합니다.$ sudo gem install cocoapods 그리고 CocoaPods Master Specs repository에 있는 Podspec file를 컴퓨터에 다운로드합니다. –verbose 명령어를 이용해 현재 진행 상황을 터미널에서 볼 수 있게 합니다.$ pod setup --verbose 이제 코코아팟을 사용할 준비가 되었습니다. Xcode에서 간단한 프로젝트를 만들고 끝냅니다. 이번 글에서는 관광명소를 보여주는 목록 앱을 예제로 만들겠습니다.2.라이브러리 연결하기터미널 프로그램을 이용해 방금 전 만든 프로젝트 경로로 이동하고, Podfile을 만들어 앱에 필요한 라이브러리를 설정합니다. Podfile을 만드는 방법이 두 가지입니다. 첫 번째는 pod init 명령어를 이용해 코코아팟이 기본 틀이 있는 파일을 생성하게 하는 것입니다. 두 번째는 개발자가 직접 빈 파일을 만들어 설정하는 방법입니다. 이번 글에서는 pod init 명령어를 사용하겠습니다. (편리합니다.)$ pod init podfile이 생성된 것을 확인할 수 있습니다.이제 Podfile을 열어 우리가 사용할 라이브러리를 세팅하고 코코아팟 공식 사이트에 접속합니다. 사용하고자 하는 라이브러리를 검색하고 이름 옆 클립보드 아이콘에 마우스 포인터를 올려보세요. Podfile에 복사할 텍스트가 나타날 겁니다. 이 텍스트를 복사하여 Podfile에 붙이고 저장합니다. 이 글에선 URL에서 가져올 이미지를 다루기 위해 SDWebImage 라이브러리를 사용하겠습니다.완성된 Podfile의 모습위의 Podfile을 잠시 설명하자면 프로젝트의 배포 타겟은 iOS 9.0 입니다. ‘use_frameworks!’ 은 코코아팟을 통해 프로젝트에 추가할 라이브러리가 스위프트로 작성되어 있고, 프레임워크를 사용할 것이기 때문에 꼭 추가해야 하는 문장입니다. 라이브러리 옆의 숫자는 4.3 그리고 4.4 이전까지 라이브러리 버전을 사용하겠다는 뜻 입니다. 최소한의 설정을 맞췄으니, 저장하고 다음 명령어를 실행합니다.$ pod install --verbose pod install 완료 후 xcworkspace 파일이 추가된 것을 확인할 수 있습니다.Pod 설치가 완료되면 xcworkspace 파일이 생성된 것을 확인할 수 있습니다. Xcworkspace 파일은 쉽게 말해서 프로젝트들의 컬렉션(collection of projects)입니다. 기존에 제작한 프로젝트(Original project)와 pods 프로젝트(Pods project)를 함께 묶는데, 이 pods 프로젝트 하나로 모든 라이브러리를 관리할 수 있습니다. 기존 프로젝트는 이 pods 프로젝트를 의존하기 때문에 xcodeproj 파일을 열면 연결된 라이브러리들에 대한 정보가 없어서(혹은 발견하지 못해서) Xcode 프로그램이 에러를 발생시킵니다. 그러므로 코코아팟으로 pod을 설치했을 때, 프로젝트는 xcworkspace 파일을 열어 개발해야 연결한 라이브러리들을 잘 사용할 수 있습니다.3.라이브러리 사용하기이제 연결한 라이브러리를 사용해봅시다.1) 예제에서는 SDWebImage 라이브러리를 이용해 URL 이미지 주소로 ImageView에 이미지를 설정하도록 코드를 추가하겠습니다.테이블뷰(UITableViewController) 컨트롤러를 이용해 목록으로 관광명소 이름, 설명, 이미지를 보여줄 것입니다. 관광명소 이름, 설명, 이미지에 맞게 데이터 모델을 만들고 스토리보드에서 UI를 디자인합니다. 테이블뷰 컨트롤러 파일을 새로 생성해서 이 소스 파일에서 라이브러리를 연결해서 기능을 구현해봅시다. 먼저 라이브러리를 이 소스에 연결하도록 import 명령어를 입력합니다.AttractionTableVC.swift import SDWebImage 그리고 아래와 같이 tableView(tableView:cellForRowAtIndexPath:) 함수에 코드를 작성합니다.2)override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> AttractionTableViewCell {         // Table view cells are reused and should be dequeued using a cell identifier.         let cellIdentifier = "AttractionTableViewCell"         guard let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as? AttractionTableViewCell else {             fatalError("The dequeued cell is not an instance of AttractionTableViewCell.")         }         let attraction = attractions[indexPath.row]                  // . . .         cell.attractionLabel.text = "\(indexPath.row). \(attraction.nameWithDescription)"         cell.attractionImage.sd_setImage(with: attraction.photoURL, completed: nil)                 // . . .                 return cell     } SDWebImage 라이브러리를 쓴 이유는, URL 이미지 주소를 이용해서 관광명소 이미지를 보여주고 싶었습니다. 하지만 UIImage에 바로 URL 주소를 사용할 수 없었고, Data 형식으로 변환한 다음 사용해야 했습니다. 라이브러리를 안 쓴 다면 아래와 같은 소스를 작성해야 했을 겁니다.// return UIImage which is set from url data     private func imageFromUrl(url: URL) -> UIImage {         var photo = UIImage()          do {             let imageData = try Data.init(contentsOf: url)             photo = UIImage(data: imageData)!             return photo         } catch {             print(error.localizedDescription)             return photo         }     } 하지만 위에서 만든 소스를 SDWebImage 라이브러리를 이용하면 아래처럼 딱 하나의 명령문으로 줄일 수 있습니다.cell.attractionImage.sd_setImage(with: attraction.photoURL, completed: nil) 소스 길이가 확연히 줄어들었습니다. 이외에도 GIF 지원, asynchronous image downloader 등 SDWebImage 라이브러리 GitHub 페이지로 접속하면 자세한 기능들을 만날 수 있습니다.CocoaPods Error브랜디의 앱 프로젝트를 클론해서 작업하면 종종 코코아팟 관련 오류로 당황했던 적이 있습니다. 몇 가지 에러의 해결 방법들을 소개하겠습니다.1.“/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/usr/include/sqlite3.h” not found”-> 대부분의 오류들은 코코아팟을 다시 설치하면 거의 다 해결됩니다.$ sudo gem install cocoapods$ pod install –verbose2.“Could not build module firebase core” Error-> project’s temp file 삭제 (~/Library/Developer/Xcode/DerivedData — Xcode->Preference->Location에 위치함)우선 위의 폴더 경로를 먼저 찾아 Finder로 여세요. 그 다음에 Xcode를 종료해 안전하게 삭제해야 합니다.-> ProjectName, .xcworkspace 삭제-> Podfile.lock 파일과 Pods 폴더 삭제-> $ pod install –verbose-> 새로 생성한 ProjectName.xcworkspace 실행하여 다시 빌드하기-> 그래도 안 된다면?—> $ pod update(or) —> $ pod –version 체크(or) —> $ pod repo update—> Podfile에 ‘Firebase’ 주석 처리—> $ pod install (old Firebase가 제거된다)—> Podfile에 ‘Firebase’ 주석 해제—> $ pod install (new Firebase 설치)—> 해결 완료!Conclusion이제는 새로운 기능을 개발하거나 소스를 수정할 땐 코코아팟에서 관련 라이브러리를 찾아봅니다. 마음에 드는 라이브러리는 곧바로 개발하고 있는 앱 프로젝트에 연결해 적용하기도 하고요. 자신의 언어로 순수하게 소스를 개발하는 것도 좋지만, 좋은 도구를 활용하는 것도 업무에 도움이 될 겁니다. 혹시 마음에 드는 라이브러리 찾으셨다면 저에게도 알려주세요. 코코아팟을 사용하는 iOS 개발자가 되신 걸 축하드립니다!주석 1)각 라이브러리의 GitHub 페이지에서는 소스를 연결하는 자세한 방법들을 소개하고 있다.2)attractions 배열에 미리 만들어 놓은 관광명소 데이터들을 저장한다. 배열에서 선정한 하나의 관광명소 데이터 정보를 이용해 각 테이블 뷰 셀에 알맞게 설정한다. 여기서 테이블 뷰 셀에 있는 attractionImage(UIImageView)에 URL 주소로 이미지를 설정하면 된다.참고문헌 swift3 - Error: Could not build Objective-C module ‘Firebase’ - Stack OverflowGoogle 그룹스An Introduction to CocoaPods (Route 85) - YouTube글김주희 사원 | R&D 개발1팀[email protected]브랜디, 오직 예쁜 옷만#브랜디 #개발문화 #개발팀 #업무환경 #인사이트 #경험공유
조회수 1210

공포의 Swift3

라이비오를 시작하며 이전 사업과는 다르게 어쩔 수 없이 안고 출발했던 핸디캡이 있었다."너는 개발자가 아니잖아."사실이다.아무래도 위제너레이션이나 오드리씨를 할 때는 영업과 마케팅 위주의 조직이었다보니 급하면 급한대로 내가 직접 할 수 있는 것들이 많았다. 하지만 앱 개발은 완전 다른 차원의 이야기라 (디자인은 직접 맡고 있지만) 새롭게 배워야 할 부분이 정말 많았다.'그래도 이제와서 어떻게 개발을 배우겠어.' 싶어서 공부를 시작했다가 그만두기도 몇 번.개발의 ㄱ자라도 잡아보자 싶어 HTML이나 CSS, PHP 같은 언어들을 끄적끄적 공부해보곤 했었는데, 기본서 수준이거나 Codecademy 따라해 본 것이 전부이다보니 실제로 뭔가를 해 볼 수준에는 전혀 미치지 못했다.그래서 이번에는 돈을 좀 쓰더라도 독학 말고 수업을 들어보자고 생각했다.새해를 맞아 큰 맘 먹고 백만원 상당의 패스트캠퍼스 수업을 질러 Swift3 를 배우게 되었다. (iOS 개발언어)(나만의 앱을 만들고 싶은데 넘나 어려운 것..........)벌써 오늘이 11강째인데 전체가 16강인 것을 생각하면 어느새 진도를 많이 뺐다.그런데 문제는,초기 문법 배울 때는 괜찮았는데예제 따라하기로 들어가면서부터 수준이 높아져 따라갈 수가 없다는 것이다.특히 지난 주에는 수업을 들으면서 동시에 절망하는 수준에 이르렀는데다들 아무 말도 없고 키보드 마우스 소리만 들리기에, 나만 이해를 못하고 있다는 생각에 중반부터는 아예 수업 듣기를 포기하고 조용히 yes24를 켜서 Swift 기본서를 주문했다. (빠른 상황판단ㅋㅋㅋㅠㅠㅠ)그런데 수업을 마친 후 강사님이 "오늘 너무 빨랐나요?"하고 물으니,수강생들이 너도나도 손을 들며 너무 빨랐다고, 놓쳤다고 얘길 하는 게 아닌가!나만 놓친게 아니라는 (어리석은..) 잠깐의 위로를 받았다.하지만 설날이 지나고,이제 좀 더 쉬워졌게지 하는 생각으로 오늘 11강에 들어갔더니 웬걸.여전히 어렵고, 이해가 0되는 현상이 발생했다.역시 공부에는 지름길도 속임수도 없다.아무래도 Swift 책을 때며 따로 복습해야 따라갈 수 있을 것 같다.슬프게도 두께가 이만큼이다. (1473페이지 중에 226페이지까지 복습했다^^............)*강사님이 쓰신 책도 있지만 Objective C 라는 다른 언어와의 비교를 중심으로 설명하셔서 초심자에게는 오히려 어려운 부분이 있었다. 아예 초보에게는 '꼼꼼한 재은씨의 스위프트3 (기본편)' 책을 추천한다. 제목만큼 꼼꼼하게 쓴 책이다. (홍보는 아닙니다만 책이 너무 좋아서 구매링크)뭐 하나 쉬운 것이 없지만, 그래도 서른이 되기 전에 좀 더 제대로 코딩 공부를 해 보게 되어서 다행이다.언어 + 수학 + 논리의 결합인 코딩은 어렵지만 꽤 아름답다.난 문돌이라 못 한다고 한계만 짓지 않으면 충분히 할 수 있을 것 같다.대신 만만하게 생각하지는 말고 꾸준히...........올해 안에 꼭 내 이름으로 된 앱 출시를 해봐야겠다.#라이비오 #비전공개발자 #iOS #Swift #인사이트 #경험공유
조회수 1535

Humans of TODAIT : 전설의 전성윤을 만나다

‘Humans of TODAIT’의 두번째 주인공, 투데잇 안드로이드 개발자 전성윤을 만나봤습니다. 투데잇의 전설(★)이 된 그의 이야기를 함께 들어볼까요?(2016.08)✓ 전설윤, 그는 누구인가Q. 자기소개 부탁드려요.안녕하세요! 투데잇에서 안드로이드 개발자를 맡았던 전성윤입니다. 퇴사자 인터뷰를 하려니까 투데잇을 떠나는 게 정말 실감나네요. 작년 2015년 10월 경, ‘SW 마에스트로’ 과정에서 만난 분께서 대표님을 소개해주셨고, 좋은 인연으로 연결되어 투데잇과 함께 일하게 되었어요. 처음엔 안드로이드 개발자로 들어왔다가 지금은 안드로이드 개발 팀장직까지 맡고 있습니다. (당시 2016.08)Q. 투데잇을 떠나는 이유는 무엇인가요?사실 처음 입사할 때도 1년 정도 생각하고 있었어요. 일하다보니 투데잇이 너무 좋아져서 나가고 싶지 않았지만, 아무래도 군문제와 학교 복학 문제가 마음에 걸리더라고요. 그래서 여러 고민 끝에 할 수 없이 나가게 된 케이스예요. 결국 아쉽지만 이번 8월을 마지막으로 투데잇과 헤어지게 되었죠.Q. 별명이 전설윤이라고 하던데, 왜 전설이라고 불리는 건가요?저도 이번 인터뷰를 통해서 생각해보게 된 건데요. ‘전설’이라는 칭호가 지금은 한물 간 사람에게 붙는 것이 아닌가 싶어요. 다른 팀원분들이 들어오시게 되면서 자연스럽게 리즈시절이 지나게 되었죠. (웃음)다른 이유가 아니라, 혼자만 잘하는 것이 아닌 다 함께 잘하기 위해 애썼기 때문인 것 같아요. 업무에 있어 버려야 할 습관 같은 작업 처리 팁에 대해 새 팀원분들께 적극적으로 공유했고 적당한 선에서 체크해드렸어요. 7–8개월의 짬 덕분인지 기획 단계에서부터 개발 단계 때 준비할 것들이나 구현 방법들이 곧장 떠오르는 경지에 이르더라고요. 이런 점들이 다른 팀원분들이 좋은 퍼포먼스를 낼 수 있게 이끌어주는 역할을 했던 것 같아요. 또 그 분들도 잘 하는 방법에 대해 치열하게 고민했고 실제로도 다들 너무 잘해주셔서 결론적으로 이제는 혼자가 아닌 다 함께 잘하게 된 거죠.✓ 보안 전문가에서 투데잇 안드로이드 팀장으로 레벨 업!Q. 투데잇 안드로이드 팀장까지, 성윤님의 입사 초반부터 지금까지 업무 성장과정이 듣고 싶어요!입사 초기에는 솔직히 맘고생 많이 했어요. 내가 지금 당장 회사에 기여할 수 있는 점이 무엇일까 의욕적으로 많이 고민했지만, 그에 비해 개발 능력은 많이 떨어졌죠. 초기에는 무엇보다 제 개발 능력 수준에 대해 정확히 몰랐고 커뮤니케이션하는 방법이 미숙했던 것 같아요. 그 때마다 대표님, 개발팀장님께서 진심어린 피드백과 따끔한 조언을 계속 해주셔서 해결할 수 있었죠. 두 분의 노력덕분에 중반부터는 업무 처리 능력도 커뮤니케이션 능력도 많이 향상됐어요,이후에 본격적으로 개발자분들이 더 들어오고 협업이 시작되면서 1인 개발자에서 2인 개발자 체제로 바뀌게 되었고, 자연스럽게 그에 따른 새로운 이슈들이 생길 수 밖에 없었어요. 다른 개발자분들에게 업무 분담하고 리드하는 부분에서 어려움을 느꼈지만, 함께 잘 해결해나갈 수 있었죠 지금은 기획적인 틀을 잡고 누군가에게 일을 맡기고 내 일을 해내고 하는 여러 부분에서 팀장의 위치에서 역할을 잘 해내는 것 같아요. (웃음)Q. 그렇다면 특별히 힘들거나 어려웠던 일정은 무엇이 있었나요?되게 좋은 질문이에요! 전 개발자다보니까 이런 질문이 너무 좋네요. (웃음)구매페이지와 그룹 기능 작업이 좀 힘들었어요. 그 중에서도 프로버전 구매페이지 작업이 가장 힘들었는데요. 작업 자체가 힘들다기보다는 구매페이지에서 에러가 나면 유저의 신뢰도에 큰 영향을 주기 때문에 한치의 실수도 용납되지 않는 부분이라는 점이 부담이 됐죠. 처음엔 실수도 많았는데, 그 이후에 치밀하게 설계한 덕분에 두 번째부턴 버그가 터져도 바로 대처할 수 있었어요. 개인적으로 개발적으로 많이 성장한 느낌을 받았죠.그룹 기능 개발은 클라이언트 쪽에서 해결해야 하는 부분이 많아서 힘들었어요. 하지만 릴리즈 이후 유저분들이 격렬하게 환호해주신 덕분에 뿌듯하게 잘 마무리할 수 있었죠. 두 작업 다 힘들었지만, 굉장히 보람됐던 작업으로 기억에 남아요.Q. 유저들의 피드백을 보면서 얻은 게 많다고 하던데, 좀 더 말해주세요!“실제 유저와 소통하고 친근한 서비스를 제공하려고 노력하다보니 유저들의 피드백이 얼마나 소중한 건지 알게되더라구요.”투데잇을 다니기 전까지만 해도 저는 별 5개 짜리 리뷰가 당연하게 받아야할 칭찬이라고 생각했어요. 그래서 사실 크게 반응하지 않았었는데, 실제 유저와 소통하고 친근한 서비스를 제공하려고 노력하다보니 유저들의 피드백이 얼마나 소중한 건지 알게되더라구요. 당연히 좋은 피드백은 정말 힘이 많이 되었고, 좋지 않은 피드백도 굉장히 감사했어요. 사실 유저 입장에서 그냥 지워버리면 그만인건데, 우리 앱의 장점을 알아봐주시고, 개선할 점을 말해주시고 또 기다려주시는 거잖아요. 그런 유저분들 보면서 빨리 개선해드리고 싶단 생각이 들죠. 그 어떤 피드백보다 더 큰 동기부여가 되더라고요.✓ 투데잇 TALK“투데잇팀은 서로 부담없이 정말 효율적으로 커뮤니케이션을 해서 어떻게 하면 서로의 업무 컨텍스트를 빠르게 마무리할지 고민해요.”Q. 투데잇의 힘은 이거다!음 투데잇의 힘은 서로를 존중하고 커뮤니케이션을 중시하는 데에서 나오는 것 같아요. 결국 모든 문제의 결착점은 커뮤니케이션이거든요. 사소한 거라도 시기에 맞춰서 커뮤니케이션이 되어야 하는데, 개발팀과 비개발팀간의 커뮤니케이션이 사실 어렵잖아요. 하지만 투데잇팀은 서로 부담없이 정말 효율적으로 커뮤니케이션을 해서 어떻게 하면 서로의 업무 컨텍스트를 빠르게 마무리할지 고민해요. 누군가 못한 부분이 있더라도 절대 무시하지 않고, 서로의 업무를 존중하고 정식적으로 피드백을 공유하면서 문제를 잘 해결하기 위한 고민을 하죠.Q. 투데잇에서 제일 기억에 남는 순간이 있다면?투데잇에서는 많이 힘들었을 때부터 행복했을 때 그리고 소소한 일상들까지 전부 다 기억에 남아요. 워크샵이라고 가평에 가서 일한 적이 있었는데, 정말 놀지도 못하고 거의 일만했거든요. 근데 밖에 나와서 그런지 그 자체가 너무 재밌었어요. 일하면서도 되게 색다르고 즐거웠죠. (웃음) 제주도로 워크샵 갔을 때도 너무 재미있었고, 정말 매일 매순간이 기억에 남아요. 팀 분위기가 너무 좋아서 그런지 특별한 일들 뿐만 아니라 개발팀 회의할 때나 회사 메신저에서 웃고 떠들고 했던 것들처럼 아주 소소한 일상들까지 모두 에피소드였던 것 같아요. 깜짝 생일파티도 그렇고 되게 예정없이 나온 에피소드가 많았거 든요. 그런게 진짜 참된 에피소드 아닐까요?✓ 마지막으로…Q. 애정이 컸던 만큼 투데잇을 떠나기 많이 아쉬울 것 같아요.네 많이 아쉽죠. 전 투데잇에서 10개월 동안 정말 하루종일 개발만 했어요. 일 하는 게 너무 좋아서 거의 자취하다시피 야근도 매일 했었거든요. 좋아하는 사람과 좋은 분위기에서 재밌게 일하기도 했고, 또 어떤 목표를 이루기 위해 정말 치열하게 고민하고 일할 수 있었는데 이 부분들을 더이상 느끼지 못한다는 점이 많이 아쉬워요.음 아쉬운 사건을 말하라면, 맨 처음으로 앱 안에 코틀린을 적용해볼 때 너무 시간을 오래 끌었던 일이 있었어요. 잘 적용시킬 방법에 대해 혼자 고민하고 정리해보다가 늦어졌었는데, 다른 분들 일정에 피해를 준데다가 실수도 한두개가 아니었거든요. 그 때 제가 계획대로 잘 처리했으면 마케팅적으로나 여러 시도들을 해볼 수 있지 않았을까?하는 아쉬움이 들죠. 결국 개발자가 세운 일정에서 해내는 여부에 따라 회사에 큰 영향이 가고, 다른 팀에도 막대한 영향이 갈 수 있다는 걸 뼈저리게 깨달았어요. 그래도 이 사건 덕분에 업무적으로도 많이 성장할 수 있었던 것 같아요. 몸소 당해봤으니 그럴 수 밖에 없죠. (웃음)Q. 투데잇을 떠나며 마지막으로 하고 싶은 말이 있다면?“충분히 치열하게 일했고 다함께 즐겁게 소통하면서 일했기 때문에 언젠가 또 다시 만나지 않을까 싶어요.”개인적으로 아쉬운 점은 있지만, 충분히 치열하게 일했고 다함께 즐겁게 소통하면서 일했기 때문에 언젠가 또 다시 만나지 않을까 싶어요. 모든 투데잇 사람들과 연을 이어가고 싶기도 하고 특히 대표님께 보답하고 싶다는 마음이 크거든요. 처음 들어갔을 때, 아무 준비 안 돼있는 상태였던 절 믿어주시고 함께 일할 수 있는 기회를 주셨어요. 앞서 말한 것처럼 일하면서 초반에 실수도 많이 했는데 꾸준히 믿고 지금의 포지션까지 저를 밀어주셨다는 점이 너무 감사하거든요. 대표님께 보답하자! 투데잇에 보답하자!가 마지막으로 하고 싶은 말이에요. 저 나가더라도 아는 척해주셔야 해요..!Q. 투데잇을 꿈꾸는 개발자에게 한마디!음 투데잇에 들어오는 건 어쩌면 쉬울 수도 있어요. 저 같은 경우 30분 정도 면접을 보고 당일에 바로 함께하기로 했거든요. 하지만 중요한 건 본인이 ‘함께 성장하고 싶은 사람’인가?에 대해 생각해보셔야 해요. 또 투데잇과 방향성이 맞는지 스스로 생각해보고, 성장하기 위해선 어떤 태세를 취해야 하는지 고민해보아야 하죠. 만약 이런 성향이 맞지 않는 사람이라면 아마 들어오시더라도 투데잇과 잘 맞지 않아서 힘들 수도 있거든요. 투데잇에 들어오고 싶은 분들은 이런 부분들에 대해 한 번 깊게 고민해보셨으면 좋겠어요.#투데잇 #팀원소개 #팀원인터뷰 #팀원자랑 #기업문화 #조직문화 #개발자 #개발팀
조회수 2591

시뮬레이션에서의 Process Mining(프로세스 마이닝) 활용

시뮬레이션은 실제로 실행하기 어려운 실험을 간단히 행하는 모의실험을 뜻하며, 특히 컴퓨터를 이용하여 모의실험을 할 때는 컴퓨터 시뮬레이션이라고 일컬어집니다.  시뮬레이션은 특수한 하드웨어를 사용하는 3D 가상현실이나 비행 시뮬레이션 등 다양한 분야에 사용되고 있으며, 이벤트 중심의 로그를 다루는 프로세스 마이닝에서는 이산 사건 시뮬레이션을 중심으로 연구가 이뤄지고 있습니다.이산사건(discrete event) 시뮬레이션은 시간이 경과함에 따라 시뮬레이션 이 진행되는 것이 아니라 시스템 외부 혹은 내부에서 사건이 발생했을 때만 모델을 실행시킵니다. 이산사건 시뮬레이션에서 사건이란 시스템의 외부 혹은 내부에서 발생하는 추상적인 신호를 말하며, 이산 사건이란 임의의 시각에 불규칙으로 일어나는 사건을 의미합니다.이산 사건 시뮬레이션 모델을 잘 만들기 위해서는 사건 시간과 사건에 대한 정확한 기술이 필요한 데, 이를 위해 프로세스 마이닝이 사용될 수 있습니다.[그림] 프로세스 마이닝 기반의 시뮬레이션 모델 도출 (Discovering Simulation Model, Rozinat et a l., 2009)이것은 기존에 시뮬레이션 모델링이 현실 세계에서의 관찰 및 수작업에 의해 이뤄졌다면, 좀 더 쉽고 정확한 모델링을 위해서는 데이터 기반의 AS-IS 프로세스 파악에 능한 프로세스 마이닝을 사용해 볼 수 있지 않을까 하는 의문에서 출발합니다.아래 표와 같이 프로세스 마이닝과 시뮬레이션은 AS-IS 모델과 TO-BE 모델 각각의 영역에서 서로 보완하는 역할을 담당하고 있습니다. [표] 프로세스 마이닝과 시뮬레이션 단계별 역할 비교단계프로세스 마이닝 (AS-IS)시뮬레이션 (TO-BE)프로세스 설계프로세스 마이닝을 통해 도출한 실제 프로세스 모델을 바탕으로 프로세스 (재)설계다양한 대안 모델에 대한 검증 수행구현 및 실행구현하고자 하는 프로세스 모델의 표준 모델 준수 여부 확인시뮬레이션을 통해 테스트 및 검증 완료된 프로세스 모델 구현모니터링 및 분석표준 모델 준수 모니터링 및 병목 지점, 재작업 구간 분석시뮬레이션을 통한 병목 개선 구간 및 자원 수요 예측, 작업 시간 효율화 효과 분석 이러한 연구들을 바탕으로 최근에는 생산 공정 내 작업 현황 파악 및 성과 측정을 위해 생산 시스템의 이벤트 로그를 저장하고 분석하여, 제조 공정에 대한 시뮬레이션 모델 요소를 도출하려는 연구가 진행되고 있습니다. 이를 통해 프로세스 마이닝에서 찾은 병목 구간 등 문제점을 바탕으로 어떻게 개선할 것인지, 프로세스 변경 혹은 개선이 어떤 결과로 이어질지 What-if 분석을 통해 의사 결정을 위한 예측 방법이 제공되고 있습니다. 시뮬레이션 수행의 결과로 많은 수행 결과가 출력되며, 좀 더 나아가 사건과 이벤트에 대한 상세 기록들이 로그 데이터 형태로 나올 수 있습니다. 시뮬레이션이 가상 현실이라는 관점에서 현실에 대한 프로세스 마이닝 분석은 가상 현실에 대해 마찬가지로 유효합니다. 실제로 시뮬레이션 모델링을 하고 나서 시뮬레이션 모델링이 현실을 반영할 수 있도록 잘 되었는지 검증할 필요가 있는데, 시뮬레이션 로그에 대한 프로세스 마이닝 분석을 통해 해당 프로세스 모델을 도출할 수 있습니다.  얻어진 모델을 현실 세계에서 얻어진 프로세스 모델과 동일한 기준에서 비교하고 이에 대한 차이를 다시 시뮬레이션 모델이 반영하는 순환적 구조를 통해 좀 더 정확한 시뮬레이션 모델을 얻게 됩니다.  [참고 문헌]https://en.wikipedia.org/wiki/Simulation#퍼즐데이터 #개발팀 #개발자 #개발후기 #인사이트
조회수 2669

PHP CI 환경에서 완전한 Vue 사용하기

편집자 주Vue 또는 VUE로 혼용하나 공식 사이트의 표기에 맞춰 아래와 같이 통일함-Vue-Vuex-Vue-Router목차1.Controller2.VIEW3.webpack Vue 소스 진입점4.webpack 설정5.Package.json6.Vue-Router7.Vuex8.공통 처리 mixin9.요약10.마치며시작하며드디어 브랜디 관리자 서비스에 Vue를 도입하고자 떠났던 여정의 마지막 장입니다. 브랜디 관리자 서비스는 PHP Codeigniter와 jQuery로 구성되어 있습니다. 사실 잘 운영되고 있는 서비스에 리스크가 큰 신기술을 도입하는 것은 도박에 가깝습니다. 몇 시간만 운영이 정지되어도 회사에 엄청난 피해를 안겨줄 수도 있으니까요. 하지만 여러 번의 검증과 실험으로 도박에서 이길 확률을 100%에 가깝게 끌어올린다면 한번 도전해볼 만하지 않을까요?이전 글인 PHP Codeigniter 환경에서 VUE 사용해보기에서 기본적인 webpack + Vue + Codeigniter 환경 구축 방법을 알아봤는데요. 하지만 단순히 webpack과 Vue만 적용했다고 해서 “우리 시스템은 UI 프레임워크로 Vue를 사용하고 있습니다.”라고 말할 순 없습니다. 아주 중요한 숙제가 남았죠.Vue에는 활용도를 대폭 끌어올려주는 Vue-Router와 Vuex Store1)가 있는데 그중 Vue-Router를 이번 글에서 자세히 다루려고 합니다.2) Vue-Router는 Vue.js의 공식 라우터입니다. 공식 홈페이지의 소개는 아래와 같습니다.중첩된 라우트/뷰 매핑모듈화된, 컴포넌트 기반의 라우터 설정라우터 파라미터, 쿼리, 와일드카드Vue.js의 트랜지션 시스템을 이용한 트랜지션 효과세밀한 내비게이션 컨트롤active CSS 클래스를 자동으로 추가해주는 링크HTML5 히스토리 모드 또는 해시 모드(IE9에서 자동으로 폴백)사용자 정의 가능한 스크롤 동작한마디로 정리하면 입력된 URL에 반응해 부분에 해당 URL의 view를 보여주는 기능인 것입니다. 다시 말해 URL이 변경될 때 한 페이지에서 화면 전체를 갈아끼우거나, 화면의 일부분(부분)을 치환해주는 역할을 한다는 것이죠. 더 나아가 해당 화면이 로드되기 전후로 전처리, 후처리 기능까지 가능합니다.착안점Vue와 Vue-Rotuer를 알게 되었을 땐 PHP 기반 프로젝트에 Vue-Router를 적용할 수 없으니 처음부터 새로 만들어야 한다고 생각했습니다. 로그인 인증 문제, 메뉴의 권한 관리 등 모든 것이 Vue 아래에 있어야 한다고 생각했기 때문입니다.어느 날 관리자 서비스에 TDD를 구현해보려고 Python Flask + webpack + Vue 프로젝트를 구성하고 있었습니다. 그러던 중 우연히 Flask + Vue-Router에서 404 페이지를 처리하려면 Flask Fallback 페이지를 Vue-Router 메인 페이지(가 있는 페이지)로 보내고, Vue-Router에서 진짜 매핑된 URL이 없으면 404 처리를 하는 식으로 구성한다는 글을 읽고 문득 호기심이 생겼습니다.‘관리자 서비스에서도 컨트롤러로 여러 URL을 한 가지 페이지로 보낸다면?’PHP를 거쳐 페이지로 이동한 것이므로 권한 관리와 메뉴 트리까지는 PHP에서 처리되면서 URL이 변할 것이고, 실제로 화면을 보여주는 Contents 영역만 를 사용한다면 어떻게 될지 궁금해졌습니다. 바로 하던 일을 멈추고 관리자 소스에 Vue-Router를 활용한 테스트 소스코드를 작성해봤습니다.예상했던 대로 PHP의 로그인 인증 처리를 거치면서 실제로 보이는 부분에는 부분만 정상적으로 치환되었습니다. 이 간단한 실험을 바탕으로 통계 시스템의 일부를 구현하는 데에 Vue-Router와 Vuex Store, 공통 처리 Mixin을 추가해 제작했습니다.1.Controller4개의 페이지를 가진 통계 시스템의 Codeigniter 컨트롤러 모습입니다. 기존의 서비스 URL들이 존재하기 때문에 Fallback을 통으로 Vue-Router로 보낼 순 없었고, 라우터를 사용할 페이지들을 하나의 페이지로 보냈습니다.1-1) /application/controllers/[컨트롤러 경로]... 생략 /* [라우터 view]에서 태그를 포함하고 있습니다. */ public function salesAnalysisProduct() { $this->load->view('[라우터 view]'); } public function salesAnalysisSeller() { $this->load->view('[라우터 view]'); } public function trendAnalysisProduct() { $this->load->view('[라우터 view]'); } public function trendAnalysisSeller() { $this->load->view('[라우터 view]'); } ... 생략 2.VIEWCodeigniter 환경에 반영하는 것이므로 CI에서 인식시킬 PHP view와 webpack에서 빌드 결과를 자동으로 바인딩할 html 파일로 구성됩니다.2-1)/application/views/[Vue용 view 경로]/index.php// [index.php] Vue 를 매핑할 php파일 컨트롤러의 view로딩용 [라우터 view]입니다. ... header, menu 생략 ... //바인딩될 부분 //자동매핑 html 인클루드 <?php include('index.html'); ?> ... footer 생략 ... 2-2)/application/views/[Vue용 view 경로]/index.html webpack의 빌드 결과로 자동으로 생성되는 파일입니다. [removed][removed] 위는 webpack의 HtmlWebpackPlugin에 의해 자동으로 바인딩된 모습입니다. 빌드되기 전 index.html은 다음 항목에 있습니다.3.webpack Vue 소스 진입점관리자에서는 프로젝트 폴더 안에 webpack과 Vue 용 서브 폴더를 두고 webpack.config.js에서 output 옵션을 통해 빌드 결과를 삽입하는 구조입니다. webpack 루트 폴더는 application 폴더와 같은 레벨에 위치하며, 폴더 구조나 파일 위치는 어디에 둬도 상관없습니다. webpack.config.js에서 entry 속성으로 잡아주시면 됩니다.3-1)/[webpack루트]/index.html// HtmlWebpackPlugin으로 스크립트를 삽입하기 위한 빈 템플릿 파일 3-2)/[webpack루트]/index.js/** * 진입용 index.js */ import Vue from 'vue' import axios from 'axios' import router from './router' import App from './App.vue' Vue.prototype.$http = axios new Vue({ el : '#app', router, components : { App }, template : '' }); 3-3)/[webpack루트]/App.vue [removed] import mixin from './common/common-mixin.js' import store from './vuex/store' export default { store, name : 'App' } [removed] Vuex와 통신 모듈 axios, Vue-Router 등을 루트 Vue 객체에 추가해줍니다. 브랜디 관리자의 webpack은 babel을 사용하고 있기 때문에 위의 store처럼 축약해서 작성하면 빌드된 파일에는 store: store와 같이 입력됩니다.Vue-Router는 태그에 자동으로 매핑되며, 위와 같은 구조로 상위 컴포넌트에서 할당되어 있어야 합니다. Vuex와 Vue-Router 설정은 글 아래에서 다루겠습니다.4.Webpack 설정이번에 Vue-Router와 Vuex를 도입하면서 webpack의 설정도 실제 서비스용과 개발용으로 분리했습니다. 폴더는 편의상 추가하였으며, package.json에서 자신이 설정한 경로로 설정하면 됩니다.Webpack 설정 파일은 Webpack의 시작과 끝이라고도 할 수 있습니다. Webpack 설정 파일에서 빌드할 소스의 경로와 빌드 결과 파일의 명명 규칙을 정하고, html 파일에 스크립트파를 자동으로 주입시키거나, Babel 플러그인을 통해 최신 스크립트 작성법을 브라우저를 신경쓰지 않고 사용할 수도 있습니다.그중에서도 중요한 옵션이 있는데 바로 Code Splitting에 관련된 옵션입니다.관리자 초기 Vue 모델에는 Vue-Router가 없었기 때문에 js 번들 파일의 크기가 그렇게 크지 않았습니다. 하지만 Vue-Router를 사용해 싱글 페이지 어플리케이션이 되거나 화면의 UI가 복잡해 컴포넌트 수가 많아지면 번들 js 파일의 크기가 매우 커집니다. 즉, 캐시를 사용하지 않는 익스플로러라면 소스에서 한 글자만 바뀌더라도 모든 페이지에서 거대한 번들 js를 새로 로딩하게 되고, 상당한 서버 자원을 소모합니다.Code Splitting 적용 전위의 이미지는 Code Splitting을 적용하기 전의 번들 js 정보입니다. 실제로 완성된 Vue 프로젝트의 번들 js는 더욱 큽니다. 정말 단순한 페이지 하나를 띄우는데 매번 뚱뚱한 js를 로딩해야 하는 것은 서비스 제공자와 서비스 사용자를 모두 괴롭게 할 것입니다.Code Splitting 적용 후하지만 위처럼 작은 조각으로 나눠 필요한 시점마다 필요한 번들 js만 로드하면 매우 빠른 페이지를 제작할 수 있습니다. 따라서 Code Split 기능은 매우 중요한 이슈입니다.물론 개발을 진행하다 보면 역시 어느 것 하나 쉽게 넘어가지지 않습니다. 관리자의 웹팩은 4.x 버전대를 사용하고 있습니다. 예전에 TF에서는 Webpakc 3.x 버전대를 사용하였는데 당시에는 CommonChunkPlugin 설정을 통해 Code Splitting을 사용할 수 있었습니다. 그대로 관리자에 적용하려 했는데..Removed라고 쓰여 있습니다. 찾아보니 CommonChunkPlugin이 옵티마이즈 옵션 하위의 splitChunk 속성으로 들어가면서 설정 방법이 바뀌었더군요. 머리를 싸매고 설정을 잡습니다.4-1) /[webpack루트]/build/webpakc.config.js : 공통 설정파일'use strict' const HtmlWebpackPlugin = require('html-webpack-plugin'); const { VueLoaderPlugin } = require('vue-loader'); const path = require('path'); module.exports = { entry: { //string, object, array 가능 - 기본은 ./src app: path.join('[스크립트 파일 경로]', 'index.js') //진입점 스크립트 파일입니다. }, output: { path: '[빌드된 js 목적지 경로]', publicPath: '[이미지등의 웹상 리소스 경로]', filename: './[name].[chunkhash].js', // 엔트리 파일명명규칙 chunkFilename: '[id]_[chunkhash].js', // chunk파일 명명 규칙 // --mode development에서는 [id]에도 chunkName들어갑니다. }, //vue와 js, css 로드 규칙을 설정합니다. module: { rules: [ { test: /\.vue$/, loader: 'vue-loader', include: [ /[Vue 소스 경로]/ ] }, { test: /\.js$/, use: { loader: 'babel-loader?cacheDirectory', }, include: [ /[Vue 소스 경로]/ ] }, { test: /\.css$/, oneOf: [ { use: [ 'vue-style-loader', 'css-loader' ] } ] } ] }, resolve: { alias: { '@': '[Vue소스 경로]', // 편의상 소스단축경로를 설정합니다. }, //파일 확장자 자동인식 임포트시 해당 확장자는 생략가능합니다. extensions: ['.js', '.vue', '.json'], }, plugins: [ // Vue 파일 로더 new VueLoaderPlugin(), // html 자동 바인딩 // 아래의 플러그인으로 인해 index.html에 해시네임으로 빌드된 index.js가 자동으로 매핑됩니다. new HtmlWebpackPlugin({ // index.php에서 include할 파일이 생성될 경로와 파일명 입니다. filename: path.join('[View경로]', 'index.html'), // 자동으로 매핑할 진입점파일을 지정합니다. template: path.join('[Vue소스 경로]', 'index.html'), inject: true, minify: { removeComments: true, collapseWhitespace: true, removeAttributeQuotes: true } }), ], optimization: { //웹팩 4.x 버전에서 옵티마이즈 속성으로 추가된 CodeSplitting 기능입니다. splitChunks: { //initial - static파일만 분리, async - 동적로딩파일만 분리, all - 모두 분리 chunks: 'async', minSize: 30000, minChunks: 1, maxAsyncRequests: 5, //병렬 요청 chunk수 maxInitialRequests: 3, //초기 페이지로드 병렬 요청 chunk수 automaticNameDelimiter: '_', //vendor, default등 prefix 구분문자 (default : '~') name: true, //development모드일때 파일에 청크이름 표시여부 cacheGroups: { default: { minChunks: 2, //2개 이상의 chunk priority: -20, reuseExistingChunk: true //minChunks이상에서 사용할경우 공통사용 }, //axios, vue 같은 공통 모듈은 vendor로 관리합니다. vendors: { test: /[\\/]node_modules[\\/]/, priority: -10 } } } } }; 4-2) /[webpack루트]/build/webpack.dev.config.js 개발용 설정 파일 (네이밍은 자유)'use strict' const merge = require('webpack-merge') const webpack = require('webpack') const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin const baseWebpackConfig = require('./webpack.config') const config = require('../config').dev //개발용설정 const devWebpackConfig = merge(baseWebpackConfig, { //mode는 chunk[id], 디버깅 코드 등에 영향을 줍니다. webpack 3.x 버전에서는 Env 속성을 통해 관리했다고 합니다. mode: 'development', plugins: [ new BundleAnalyzerPlugin(), //번들 무게 분석기 제대로 스플릿 되었는지 확인할 때 사용합니다. new webpack.DefinePlugin({ env : config.env }), ], watch: true, //코드의 변화를 감지해 자동으로 재빌드해주는 옵션입니다. cache: true, //캐시 사용을 활성화하면 변경사항이 있는 코드만 재빌드합니다. optimization: { //uglify 플러그인 코드 압축 여부를 설정합니다 압축 시 용량을 매우 줄일 수 있으나 빌드 속도가 크게 저하되므로 개발 시에는 꺼줍니다. minimize: false, } }) module.exports = new Promise((resolve, reject) => { resolve(devWebpackConfig); }) 4-3) /[webpack루트]/build/webpack.prod.config.js 서비스용 설정파일 (네이밍은 자유)'use strict' const merge = require('webpack-merge') // 설정파일 결합에 사용합니다. const webpack = require('webpack') const baseWebpackConfig = require('./webpack.config') //베이스 설정파일 const config = require('../config').prod //서비스용 설정 const prodWebpackConfig = merge(baseWebpackConfig, { mode: 'production', //chunk[id], 디버깅 코드등 영향 있음 plugins: [ new webpack.DefinePlugin({ env : config.env }), ], //개발용과 반대로 용량은 줄이고 필요 없는 기능은 꺼줍니다. watch: false, cache: false, optimization: { minimize: true, } }) module.exports = prodWebpackConfig 5.package.json웹팩 설정 파일이 분리되면서 package.json의 런 스크립트도 변경했습니다.... "scripts": { "build": "webpack --config build/webpack.prod.config.js --progress", "build-dev": "webpack --config build/webpack.dev.config.js --progress" }, ... 6.Vue-RouterVue-Router는 위에서 설명한 대로 Vue의 컴포넌트와 밀접하게 결합된 라우터입니다. 그런데 여기서 webpack의 Code Split을 사용하려면 컴포넌트 import 방법이 매우 중요한데요.import './testComponent' 위처럼 import 구문을 사용해 컴포넌트를 불러오면 코드가 쪼개지지 않고 한 덩어리로 빌드되므로 아래와 같은 형태로 사용해야 합니다.const testComponent = () => import('./testComponent') webpack 공식 문서에도 나와있듯이 위처럼 ES2015 Loader spec에 있는 import()를 사용하여 컴포넌트를 생성해야 번들 js가 제대로 분리되며, Dynamic Import가 가능해집니다.Vue-Router를 쓰는 순간 싱글 페이지 어플리케이션이 되기 때문에 이곳에서 설정을 잘못 잡아주는 순간 육중한 컴포넌트 한 덩어리가 튀어나오면서 Code Splitting은 물거품이 되어버립니다. 조심합시다!또한 import 함수 안쪽엔 아래와 같은 주석을 달아야 청크 이름이 적용됩니다.const testComponent = () => import( /* webpackChunkName: '[청크이름]'*/ './testComponent') 라우터 경로 속성인 path 와 Codeigniter의 컨트롤러 경로를 맞춰주는 것이 포인트입니다!6-1) /[webpack루트]/router/index.js - 경로와 파일명은 자유입니다!import Vue from 'vue' import Router from 'vue-router' // 주석의 webpackChunkName = 코드 스플릿 chunk Name으로 사용됩니다. // 꼭 컴포넌트와 청크 이름을 같게 설정할 필요는 없습니다. const SalesAnalysisProduct = () => import(/* webpackChunkName: "salesAnalysisProduct" */ '[컴포넌트 파일 경로]') const SalesAnalysisSeller = () => import(/* webpackChunkName: "salesAnalysisSeller" */ '[컴포넌트 파일 경로]') const TrendAnalysisProduct = () => import(/* webpackChunkName: "trendAnalysisProduct" */ '[컴포넌트 파일 경로]') const TrendAnalysisSeller = () => import(/* webpackChunkName: "trendAnalysisSeller" */ '[컴포넌트 파일 경로]') Vue.use(Router) const router = new Router({ mode: 'history', routes: [ /* 통계 */ { path: '[CI컨트롤러 url]/salesAnalysisProduct', name: 'salesAnalysisProduct', component: SalesAnalysisProduct }, { path: '[CI컨트롤러 url]/salesAnalysisSeller', name: 'salesAnalysisSeller', component: SalesAnalysisSeller }, { path: '[CI컨트롤러 url]/trendAnalysisProduct', name: 'trendAnalysisProduct', component: TrendAnalysisProduct }, { path: '[CI컨트롤러 url]/trendAnalysisSeller', name: 'trendAnalysisSeller', component: TrendAnalysisSeller }, ] }) // 아래의 함수로 전처리 후처리도 가능합니다! router.beforeEach((to, from, next) => { // ... }) router.afterEach((to, from) => { // ... }) export default router 7.Vuex앞서 Vue와 Vuex, 컴포넌트간 통신과 상태 관리에서 소개했던 상태 관리와 통신을 위한 Vuex도 추가합니다. Vuex는 하나의 Store만 쓸 경우 상태 변수의 과포화로 인해 유지 보수가 어려워질 수 있으므로 namespace: true 옵션을 통해 도메인별로 관리합니다.7-1) /[webpack루트]/vuex/store.js - Vuex 진입파일import Vue from 'vue' import Vuex from 'vuex' // 각 도메인별 store들이 들어있는 modules 를 임포트해줍니다. import * as modules from './modules/index' Vue.use(Vuex) export default new Vuex.Store({ state : { }, getter: { }, mutations : { }, actions : { }, modules : modules.default }) 7-2) /[webpack루트]/vuex/modules/index.js - 도메인별 Store 자동 바인딩 스크립트const files = require.context('.', false, /\.js$/) const modules = {} //자신(index.js)를 제외한 파일들을 파일이름을 Key로 modules에 담습니다. files.keys().forEach((key) => { if (key === './index.js') return modules[key.toLowerCase().replace(/(\.\/|\.js)/g, '')] = files(key).default }) export default modules 7-3) /[webpack루트]/vuex/modules/statistics.js(통계 store 파일) - 예시입니다.export default { namespaced : true, //해당 속성을 통해 파일명을 namespace로 사용합니다. state: { /* 상태값 및 데이터 */ ... }, getters: { }, mutations: { /* state 변경처리 */ ... }, actions: { /* 통신처리 */ ... } } namespace: true로 되어있으므로 파일명인 statistics를 namespace로 사용하게 됩니다. 따라서 store 각 항목에 대한 접근은 다음과 같이 이뤄지며 computed 속성에 state: this.$store.state.statistics 처럼 정의해두면 편리합니다.dispatch는 this.$store.dispatch(‘statistics/[action 이름]’)commit은 this.$store.commit(‘statistics/[mutation 이름]’)state 변수 접근은 this.$store.state.statistics.[state 이름]8.공통 처리 mixinapi 통신에 사용되는 통신 라이브러리와 그 라이브러리의 복잡한 설정 코드, 단순한 Toast 출력 함수, 로딩 이펙트를 보여주는 함수 등 모든 항목들이 매 페이지마다 있으면, 통일되지 못한 UI, 페이지마다 일관되지 못한 설정 등으로 휴먼 에러가 발생할 확률이 높아집니다. 유지 보수 측면에서도 비용이 높아집니다. 이러한 단순 반복 코드들은 한번만 정의하고 재사용하는 것이 바람직합니다. 나중에 수정할 때도 용이하죠.공통사항을 묶어 Vue 전역 믹스인으로 Vue 루트 객체에 추가합시다. 단, global 옵션인 만큼 조심해서 써야 합니다. 시스템에 영향을 줄 것 같으면 하위 컴포넌트 mixins 속성에 넣어 해당 스코프에서만 사용하는 것이 바람직합니다.8-1) /[webpack루트]/common/common-mixin.js (파일이름, 경로는 자유입니다!)import Vue from 'vue' import Vue from 'axios' import Cookies from 'js-cookie' const TIMEOUT = '[타임아웃 시간(ms)]' /* mixin의 기본 형태는 Vue 컴포넌트의 형태와 동일합니다. 주로 전역 통신과 상태 관리는 vuex store에서, 전역 data 속성과 전역 함수는 mixin에서 관리합니다. */ Vue.mixin({ /* 전역 사용 data속성 선언 */ data: () => { return { ... //이곳에 선언하는 data 속성은 전역에서 this로 접근 가능합니다. } }, created: function() { // 공용 axios 객체 생성 this.axios = axios.create({ timeout: TIMEOUT, withCredentials: true, //공통해더는 여기에 headers : { } }); //axios 의 success와 error를 mixin method에서 처리 하도록 등록 this.axios.interceptors.response.use(this.onSuccess, this.onError) }, /* 전역 사용 함수 선언 */ methods: { /* axios의 response handling 함수*/ onSuccess : response => { }, onError : function (error) { }, /*GET, POST 등의 통신 함수, Toast(alert) 표출함수, 에러핸들링함수 등 선언*/ /*... 내용이 너무 길어서 생략 ...*/ } }); 9.요약지금까지의 내용은 파일 경로를 토대로 요약하면 다음과 같습니다. 참고로 아래의 폴더 구조는 절대적인것은 아닙니다. 모든 폴더 구조는 자율이며, 폴더 구조에 맞게 webpack.config.js에서 조정해주면 됩니다.[프로젝트 루트] └ [웹팩 루트] └ package.json └ [Vue 소스 루트] └ [common] └ [router] └ index.js // 라우터 설정파일 - CI 컨트롤러와 url 맞춰줘야함 └ [vuex] └ index.js // 도메인별 store module export 스크립트 └ [modules] └ 도메인별 store.js └ [컴포넌트 폴더] //예시에서는 ststistics └ App.vue //진입점 vue파일 Vuex와 전역 mixin 세팅 └ index.html //index.js가 주입될 껍데기 └ index.js //진입점 js Vue-Router와 App.vue 세팅 └ [build] // 빌드파일경로 └ webpack.config.js //베이스 설정파일 └ webpack.dev.config.js //개발용 설정파일 └ webpack.prod.config.js //서비스용 설정파일 └ [application] //Codeigniter 루트 └ [controllers] └ [컨트롤러 경로] // 예시의 통계부분 └ [views] └ [웹팩빌드 결과 폴더] └ [index.php] // CI 에서 로드하는 view (index.html include) └ [index.html] // js 번들이 자동 주입된 빌드결과 파일 └ [include] └ [scripts] └ [빌드결과 js 경로] //public path 속성 경로 └ 빌드 결과 js chunk들 마치며관리자 서비스에서 완전한 Vue를 사용하기 위해 꽤 험난한 과정을 거쳤습니다. 지금도 잘 돌아가는 서비스에 리스크를 감수하면서도 새로운 것을 도입하려는 이유를 찾아야 했고, 한동안은 레거시와 Vue로 된 소스를 2중으로 개발해야 했습니다.게다가 이 글을 작성하기 시작했을 땐 Code Splitting 설정 방법이 바뀌어 적용하지도 못한 상황이었기 때문에 사실 Code Splitting 내용이 없었습니다. 그런데 글을 작성하면서 splitChunk옵션을 성공해버렸어요! 덕분에 이 글도 모두 수정해야 했죠. Vue의 도입을 고려하는 개발자분들에게 도움이 되길 바라는 마음으로 글을 마칩니다.참고1)Vuex Store는 Vue와 Vuex, 컴포넌트간 통신과 상태 관리에 자세히 정리해두었다.2) 브랜디 관리자 서비스는 jQuery로 작성되어 있다. 따라서 jQuery를 베재할 수만은 없는 상황이었다. 이에 따라 기존 jQuery 컴포넌트들에 대한 해결책은 천보성 팀장님이 작성한 JQuery 프로젝트에 VUE를 점진적으로 도입하기를 참고했다. props와 emit 기능을 이용해 jQuery로 제작한 컴포넌트를 깔끔하게 Wrapping 하는 방법에 대해 자세히 기술되어 있으며, 이를 활용하면 레거시 UI 플러그인을 마치 네이티브 Vue 플러그인처럼 사용할 수 있다.글강원우 과장 | R&D 개발2팀[email protected]브랜디, 오직 예쁜 옷만

기업문화 엿볼 때, 더팀스

로그인

/