스토리 홈

인터뷰

피드

뉴스

조회수 2363

Activation Function

Activation Function(활성함수)인공신경망을 공부하다보면 활성함수(activation function)라는 것을 만나게 됩니다. 대부분의 분들은 처음 공부를 시작할 때, 저와 마찬가지로 활성함수는 그냥 이런 거구나 하신 뒤에 넘어가고 있을 거라 생각합니다. 하지만 딥러닝을 좀더 공부하다보면 어떤 활성함수를 사용했는지, 혹은 사용하지 않았는지로 인해 다양한 문제가 발생하곤 합니다. 특히 요즘 핫한 deep neural network 에서는 활성함수가 어떤 것인가에 따라서 vanishing gradient 문제로 인해 학습의 정도가 달라지기도 합니다. 이러한 이유에서 이번 포스팅에서는 활성함수를 자세히 이해해보도록 하겠습니다.인공신경망이 사람의 신경구조를 모방하여 만들어졌다는 사실은 다들 알고 계실겁니다. 인공신경망의 가장 기본 개념은 단일 퍼셉트론에서 출발했습니다. 관련된 포스팅에서도 설명했지만 퍼셉트론은 여러 개의 신호가 들어오면 이를 조합하여 다음으로 신호를 보낼지 말지를 결정합니다(0 또는 1). 이것을 발전시킨 feed forward multiple layer neural network는 하나의 단일 뉴런에 여러 신호가 들어오면, 다음 뉴런에 보낼 신호의 강도를 결정하게 됩니다. 즉, 단일 퍼셉트론이 multi layer perceptron으로 발전해나가는 과정에서, 뉴런은 신호의 전달유무가 아닌 전달 강도를 정하게 되었습니다. 이때 전달하는 신호의 세기를 정하는 방법이 활성함수입니다.많은 분들은 대표적인 활성함수로 sigmoid를 떠올리실 것입니다. 활성함수의 개념을 잡기에는 이만큼 좋은 함수가 없기 때문입니다. 그럼 우선 활성함수의 가장 기본적인 개념을 sigmoid를 통해 알아보도록 하죠. 그 전에 여러분의 이해를 돕기 위해 로지스틱 회귀분석에 대해 먼저 알아보겠습니다.로지스틱 회귀분석(logistic regression)로지스틱 회귀분석은 generalized linear model입니다. 정확히 말하자면 generalized linear model이라는 큰 개념의 여러 케이스 중 하나라고 볼 수 있겠네요. 로지스틱 회귀분석의 목적은 독립변수의 선형결합으로 종속변수인 ‘어떠한 사건이 발생할 확률’을 알고자 하는 것입니다. 어렵죠..? 쉬운 예시를 하나 들어보겠습니다.우리는 어떠한 연구를 통해 1일 흡연량과 폐암 발생 여부의 관계를 알고싶습니다. 이때 가장 쉬운 방법은 1일 흡연량{x}과 폐암 발생확률{p(y)}이 선형 관련성이 있다고 보고, 선형 회귀 분석(linear regression)을 시행하는 것입니다. 그 결과, p(y)=0.02x+0.1<math>p(y)=0.02x+0.1</math> 이라는 식이 도출되었다고 생각해보죠. 이 식은 담배를 전혀 안 피우는 사람은 10%의 확률로 폐암에 걸리고, 하루에 담배를 1개비씩 더 피울 때마다 폐암에 걸릴 확률이 2% 증가한다는 의미입니다. 표면적으로 보았을 때는 꽤나 합리적으로 보입니다. 하지만 과연 이 식을 실제 예측에 활용해도 전혀 문제가 없을까요? 예상하셨겠지만, 그렇지 않습니다.담배는 한 갑에 20개비가 들어있고, 3갑이면 60개비가 들어있습니다. 따라서 하루에 담배를 3갑 피우는 사람은 0.02∗60+0.1=1.3<math>0.02∗60+0.1=1.3</math>, 즉 130%의 확률로 폐암에 걸린다는 결론이 도출됩니다. 이는 확률의 공리에 어긋나는 결론입니다. 따라서 과거의 수학자들은 선형이라는 이해 및 계산이 쉬운 방법을 그대로 유지하면서 확률의 공리에 어긋나지 않는 방법을 찾고자하였고, 다양한 방법들 중 가장 보편적으로 사용하게 된 방법이 로지스틱 함수를 연결함수로 사용한 로지스틱 회귀분석입니다.로지스틱 함수는 아래와 같이 생겼습니다.g(x)=ex1+ex<math>g(x)=ex1+ex</math>이것을 연결함수로 적용한 generalized linear model, 즉 logistic regression의 수식은 아래와 같은 형태가 됩니다.P(y|x)=eβx1+eβx<math>P(y|x)=eβx1+eβx</math>위 식을 이용하면 비로소 선형이라는 직관적인 성질을 띄면서, 결과값의 범위가 0~1로 제한되어 확률값의 예측에 사용할 수 있는 회귀식이 도출됩니다. 이 때, 위에 사용한 로지스틱 함수가 바로 우리가 활성함수로 사용하는 sigmoid function입니다. 따라서 sigmoid를 활성함수로 사용할 경우, 필연적으로 로지스틱 회귀분석과 관련이 있을 것이라고 예상할 수 있습니다. 둘 간의 관련성을 아래 그림을 통해 알아보겠습니다.여러분의 이해를 돕고자 hidden layer가 없는 가장 단순한 형태의 feed forward neural network 형태를 그려보았습니다. 위 그림을 수식으로 나타내볼까요?P(Y|X)=exp(∑2i=0wixi)1+exp(∑2i=0wixi)=11+exp(−∑2i=0wixi)<math>P(Y|X)=exp(∑i=02wixi)1+exp(∑i=02wixi)=11+exp(−∑i=02wixi)</math>즉, 위처럼 sigmoid를 활성함수로 사용한 간단한 neural network는 logistic regression과 일치합니다. 물론 계수(weight) 추정 방법은 통계학에서 기존에 행하던 방법과는 차이가 있지만, 결과적으론 비슷한 값이 추정될 것입니다. 우리는 이 그림을 통해 아래와 같은 직관을 얻을 수 있습니다.input과 weight를 곱해서 더하는 과정은 linear combination(선형 결합)이다.인공신경망의 학습은 각 뉴런에 곱해지는 ‘weight’라는 모수(parameter)를 추정(estimate)하는 과정이다.이제 눈치 채셨나요? Sigmoid를 활성함수로 사용하는 multi layer perceptron neural network의 hidden layer의 각 뉴런은 로지스틱 회귀분석을 하는 것과 정확히 일치합니다. 따라서 학습 과정에서 각 layer의 weight라는 모수를 학습을 통해 추정하는 것입니다.mlp 적용그럼 이제 위에서 배운 로지스틱 회귀분석을 mlp에 적용해보겠습니다. 우리는 단층 퍼셉트론 에서 아래와 같은 그림을 보았습니다.위처럼 선형으로 깔끔하게 분류가 가능한 문제는 활성함수가 계단함수인 단층 퍼셉트론으로도 충분히 해결할 수 있습니다. 하지만 아래와 같은 경우는 문제가 달라집니다.이러한 분류 문제는 선형으로는 불가능하며, 비선형적인 분류를 하여야 합니다. 이처럼 우리가 원하는 비선형의 분류를 하기 위하여 크게 두 가지가 필요합니다.1개 이상의 hidden layer(2개 이상의 뉴런을 포함하여야 함)비선형의 활성함수먼저 비선형의 활성함수가 필요한 이유부터 간단하게 생각해보겠습니다. 만약 활성함수가 비선형이 아니라면, 각 뉴런의 결과값은 선형결합의 선형결합이 됩니다. 따라서 아무리 multiple layer를 쌓는다고 하여도, 결과적으로 출력값은 입력값들의 선형결합이 됩니다. 즉, 층을 여러 개 쌓는 의미가 퇴색되는 것입니다.다음으로 hidden layer와 뉴런의 갯수에 대한 정의가 왜 필요한지 생각해보겠습니다. 위에서 언급하였듯이 logistic regression은 generalized linear model입니다. 여기서 ‘linear model’에 주목해주세요. 즉, logistic regression도 결국은 선형 모델이라는 것입니다. 왜일까요? Logistic regression을 이항분류 문제(결과의 범주가 0 또는 1)에 적용하여, 결과값이 특정값 이상이면 1로 분류한다고 생각해보겠습니다. 이것은 결국 기존의 단일 퍼셉트론에서 활성함수로 sigmoid를 사용한 뒤, 다시 계단함수를 적용한 것과 같습니다. 비록 우리가 sigmoid라는 비선형의 활성함수를 사용했지만, 로지스틱 함수의 지수를 풀어내면 결국 선형 결합의 결과값에 대한 분류이므로 우리가 원하는 비선형의 분류를 할 수 없습니다. 따라서 위와같은 문제를 해결하기 위하여, 비선형의 활성함수를 쓰되, 다수의 뉴런을 갖는 hidden layer를 사용하는 것입니다. 이 때, hidden layer의 뉴런 갯수가 늘어날 수록 좀더 비선형으로 데이터에 적합한 분류가 가능해지지만 overfitting 문제가 발생하게 됩니다. 따라서 hidden layer의 뉴런 갯수를 과제마다 적절히 지정해주는 것이 중요합니다.activation function의 종류마지막으로 activation function의 종류 및 특징에 대해 정리해보겠습니다.1. Sigmoid functionBy Qef (talk) - Created from scratch with gnuplot, Public Domain, Link<특징>수식 : σ(wx+b)=ewx+b1+ewx+b<math>σ(wx+b)=ewx+b1+ewx+b</math>범위 : (0,1)시그모이드 함수는 완전히 값을 전달하지 않거나(0) 혹은 완전히 전달한다(1)는 특성 때문에 실제 인체의 뉴런과 유사하다고 생각되어 널리 사용되었으나, 현재는 점차 사용하지 않는 추세입니다. 그 이유는 아래와 같습니다.Vanishing Gradient :sigmoid 함수는 뉴런의 활성화 값이 0 또는 1에 매우 가깝다면(saturate), 해당 편미분 값이 0에 매우 가까워지는 특성이 있습니다. 인공신경망의 back propagation에서 가장 일반적으로 사용되는 gradient descent의 경우 chain rule을 이용하는데, 이 과정에서 0에 매우 작은 값이 계속 곱해진다면 그 값은 0으로 점점 더 수렴합니다. 즉, 학습의 결과가 back propagation 과정에서 전달되지 못하고 이에 따라 weight 값의 조정이 되지 않습니다. 이것은 학습의 과정뿐만 아니라, 초기 weight 값을 임의로 줄 때에도 문제가 됩니다. f=σ(wx+b)<math>f=σ(wx+b)</math> 를 통해 확인해보죠. 만약 w의 값이 매우 커서 σ(wx+b)<math>σ(wx+b)</math>의 값이 1에 매우 가까워 진다면, weight값은 초기 값에서 크게 변하지 않고 학습이 되지 않을 것입니다. 그럼 우리의 신경망 모델의 정확성도 감소하겠죠. 이것이 vanishing gradient problem입니다.중심값이 0이 아니다 :Sigmoid function의 결과값은 그 중점이 0이 아니며, 모두 양수입니다. 이 경우 모수를 추정하는 학습이 어렵다는 단점이 있습니다. 하지만 이것은 다른 방식으로 모델 내에서 극복이 가능하기 때문에 vanishing gradient 에 비해 큰 문제는 아닙니다.2. tanh function<특징>수식 : tanh(x)=e2x−1e2x+1<math>tanh(x)=e2x−1e2x+1</math>범위 : (-1,1)tanh(hyperbolic tangent) function은 sigmoid 처럼 비선형 함수이지만 결과값의 범위가 -1부터 1이기 때문에 sigmoid와 달리 중심값이 0입니다. 따라서 sigmoid보다 optimazation이 빠르다는 장점이 있고, 항상 선호됩니다. 하지만 여전히 vanishing gradient 문제가 발생하기 때문에 대안이 등장하게 됩니다.3. Relu(Rectified Linear Unit)<특징>수식 : y=max(0,x)<math>y=max(0,x)</math>범위 : (0,∞<math>∞</math>)Relu는 위 그림처럼 선형그래프를 한 번 꺾은 형태입니다. 이 간단한 함수는 오랫동안 인공신경망의 발목을 잡던 vanishing gradient 문제를 해결했습니다. 하지만 여전히 장점과 단점이 존재합니다.장점기존의 sigmoid, tanh에 비해 converge되는 속도가 빠릅니다. 이것은 그래프의 형태가 선형이고, saturate problem이 발생하지 않기 때문으로 보여집니다.x값이 0을 기준으로 선형발현/미발현 이라는 간단한 형태이기 때문에 상대적으로 연산량이 많은 exponential을 사용하지 않아, 컴퓨터의 연산에 대한 부담을 줄여줍니다.단점“dying Relu problem”이 발생합니다. 만일 학습 과정에서 weight가 특정 뉴런이 activate되지 않도록 바뀐다면, 해당 뉴런을 지나는 gradient도 0이 됩니다. 따라서 training 과정에서 해당 뉴런이 한 번도 발현하지 않게 될 수도 있습니다. 심한 경우에는 네트워크 전체 뉴런의 40%가 죽어있는 경우도 발생한다고 합니다(출처 : http://cs231n.github.io/neural-networks-1/). 이것을 막기 위해서는 learning rate를 크지 않게 조절하는 것이 중요합니다. 또 다른 해결 방안으로는 leaky relu와 같은 activation function을 사용할 수도 있습니다.정리이번 포스팅을 통해 우리는 activation function이 무엇이고, 왜 필요한 것인지 알아보았습니다. 또한 어떠한 activation을 어떻게 사용해야하는지도 배웠습니다. 제가 위에 소개한 것 이외에도 다양한 activation function이 있으므로, 한 번쯤 찾아보며 공부해보시면 좋겠습니다.
조회수 763

HBase Meetup - 비트윈에서 HBase를 사용하는 방법

비트윈에서는 서비스 초기부터 HBase를 주요 데이터베이스로 사용하였으며 사용자 로그를 분석하는 데에도 HBase를 사용하고 있습니다. 지난 주 금요일(11월 15일)에 HBase를 만든 Michael Stack 씨가 한국을 방문하게 되어 ZDNet 송경석 팀장님의 주최 하에 HBase Meetup Seoul 모임을 가졌습니다. 그 자리에서 VCNC에서 비트윈을 운영하면서 HBase를 사용했던 경험들이나 HBase 트랜잭션 라이브러리인 Haeinsa에 대해 간단히 소개해 드리는 발표 기회를 가질 수 있었습니다. 이 글에서 발표한 내용에 대해 간단히 소개하고자 합니다.비트윈 서비스에 HBase를 사용하는 이유비트윈에서 가장 많이 사용되는 기능 중 하나가 채팅이며, 채팅은 상대적으로 복잡한 데이터 구조나 연산이 필요하지 않기 때문에 HBase 의 단순한 schema 구조가 큰 문제가 되지 않습니다. 특히 쓰기 연산이 다른 기능보다 많이 일어나기 때문에 높은 쓰기 연산 성능이 필요합니다. 그래서 메세징이 중심이 되는 서비스는 높은 확장성(Scalability)과 쓰기 성능을 가진 HBase가 유리하며 비슷한 이유로 라인이나 페이스북 메신저에서도 HBase를 사용하는 것이라고 짐작할 수 있습니다.로그 분석에도 HBase를 사용합니다비트윈은 사용자 로그 분석을 통해서 좀 더 나은 비트윈이 되기 위해서 노력하고 있습니다. 비트윈 사용자가 남기는 로그의 양이 하루에 3억건이 넘기 때문에 RDBMS에 저장하여 쿼리로 분석하기는 힘듭니다. 그래서 로그 분석을 위해 분산 데이터 처리 프레임워크인 Hadoop MapReduce를 이용하며 로그들은 MapReduce와 호환성이 좋은 HBase에 저장하고 있습니다. 또한 이렇게 MapReduce 작업들을 통해 정제된 분석 결과를 MySQL에 저장한 후에 다양한 쿼리와 시각화 도구들로 custom dashboard를 만들어 운영하고 있습니다. 이를 바탕으로 저희 Biz development팀(사업개발팀)이나 Data-driven팀(데이터 분석팀)이 손쉽게 insight를 얻어낼 수 있도록 돕고 있습니다.HBase를 사용하면서 삽질 했던 경험HBase를 사용하면서 처음에는 잘못 사용하고 있었던 점이 많았고 차근차근 고쳐나갔습니다. Region Split과 Major Compaction을 수동으로 직접 하는 등 다양한 최적화를 통해 처음보다 훨씬 잘 쓰고 있습니다. HBase 설정 최적화에 대한 이야기는 이전에 올렸던 블로그 글에서도 간단히 소개한 적이 있으니 확인해보시기 바랍니다.HBase 트랜잭션 라이브러리 해인사Haeinsa는 HBase에서 Multi-Row 트랜잭션을 제공하기 위한 라이브러리입니다. 오픈소스로 공개되어 있으며 Deview에서도 발표를 했었습니다. HBase에 아무런 변형도 가하지 않았기 때문에 기존에 사용하던 HBase 클러스터에 쉽게 적용할 수 있습니다. 비트윈에 실제로 적용되어 하루 3억 건 이상의 트랜잭션을 처리하고 있으며 다른 많은 NoSQL 기반 트랜잭션 라이브러리보다 높은 확장성과 좋은 성능을 가지고 있습니다.저희는 언제나 타다 및 비트윈 서비스를 함께 만들며 기술적인 문제를 함께 풀어나갈 능력있는 개발자를 모시고 있습니다. 언제든 부담없이 [email protected]로 이메일을 주시기 바랍니다!
조회수 1192

테이블이냐, 컬렉션이냐, 그것이 문제로다!(KOR)

편집자 주 외래어 표기법에 따르면 ‘원어에서 띄어 쓴 말은 띄어 쓴 대로 한글 표기를 하되, 붙여 쓸 수도 있다.’고 규정하고 있다.(제3장 제1절 영어의 표기, 제10항과, 컴퓨터 전문어, 전기 전문어 등) 즉 ‘원칙’과 ‘허용’이 모두 가능하다는 의미다. 이를 바탕으로 여러 표기 용례를 참고한 결과, TableView는 ‘테이블뷰(원칙)’로 표기해야 하나, 본문에서는 독자의 가독성을 높이기 위해 ‘테이블 뷰(허용)’로 표기한다. 응용하여, CollectionView는 ‘컬렉션 뷰’로, TableViewCell은 ‘테이블 뷰 셀’ 등으로 띄어 쓴다. Overview앱에서 데이터를 사용자에게 보여줄 땐 여러 가지의 모습으로 나타납니다. 설정 앱처럼 목록으로 보여줄 때도 있고, 사진 앱처럼 그리드(grid) 형식으로 보여줄 때도 있습니다. 이처럼 데이터를 보여줄 때 많이 사용되는 뷰는 테이블 뷰(UITableView) 또는 컬렉션 뷰(UICollectionView)입니다. 각자 특징이 있기 때문에 앱의 성격에 따라 적절한 뷰를 사용해야 합니다. 왜냐하면 목록을 보여주는 디자인을 바꿀 때, 다시 개발해야 하는 수고를 덜 수 있기 때문입니다. 이번 글에선 각각의 뷰를 간략하게 알아보겠습니다. 목록 형식의 설정 앱과 그리드 형식의 사진 앱 스크린샷테이블 뷰(UITableView)단일 열에 배열된 행을 사용해 데이터를 표시하는 뷰입니다. 수직 스크롤만 가능하며, 테이블의 개별 항목을 구성하는 셀은 테이블 뷰 셀(UITableViewCell) 객체입니다. 테이블 뷰는 이 객체들을 이용해 테이블에 표시되는 행을 그립니다. 여러 행은 하나의 섹션 안에 구성될 수 있으며, 각 섹션은 헤더(header)와 푸터(footer)를 가질 수 있습니다. 섹션과 행은 인덱스 번호로 구별하는데, 번호는 0부터 시작합니다. 테이블 뷰는 plain과 grouped 스타일 중 한 가지의 스타일을 가질 수 있습니다. Plain 스타일은 보통 목록 스타일입니다. 섹션의 헤더와 푸터는 섹션 분리기(inline separators)로 표시되고 스크롤을 할 때 해당 섹션 안에 있는 콘텐츠 위에 나타납니다. Grouped 스타일은 시각적으로 뚜렷한 행 그룹을 표시하는 섹션이 있습니다. 섹션의 헤더와 푸터는 콘텐츠 위에 나타나지 않습니다. 아래와 같은 사진을 보시면 확연히 차이를 볼 수 있습니다. plain 스타일의 연락처 앱과 grouped 스타일의 설정 앱테이블 뷰의 많은 메소드들은 인덱스패스(NSIndexPath) 객체를 매개변수 또는 리턴 값으로 사용합니다. 테이블 뷰는 해당하는 행의 색인 인덱스와 섹션 인덱스 값을 가져올 수 있게 인덱스패스의 범주를 선언합니다. 또한 색인 인덱스와 섹션 인덱스 값을 가지고 인덱스패스를 만들 수 있습니다. 특히 여러 섹션이 있는 테이블 뷰는 섹션 인덱스 값이 반드시 있어야 행의 인덱스 번호로 구별할 수 있습니다.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.image = attraction.photo                 cell.attractionImage.tag = indexPath.row                 attraction.indexPath = indexPath                 ...                 return cell     } 위의 코드는 데이터 소스(data source) 메소드로, 테이블 뷰의 특정한 위치에 셀을 추가합니다. 다시 말해, 이 메소드는 테이블 뷰가 ‘표시할 새로운 셀이 필요할 때마다’ 특정 행에 노출할 정보가 있는 셀을 만들고 리턴하는 걸 말합니다. 매개변수로 필요한 셀 객체의 행을 가리키는 indexPath 값을 전달합니다. 그리고 indexPath의 row 값을 이용해서 attraction이라는 배열 인덱스로 활용하고, 셀에 표시할 정보들을 설정합니다. 여기서 attraction 배열은 관광 명소들의 정보들이 담고 있는 배열인데, 1행은 첫 번째로 저장한 관광 명소, 2행은 두 번째로 저장한 관광 명소 등 순서대로 설정하도록 indexPath.row 값을 이용하는 것입니다. indexPath의 row 값과 배열의 인덱스 값은 0부터 시작하기 때문입니다. 해당 예제는 섹션이 1인 경우이기 때문에 섹션 인덱스 값이 없지만, 섹션이 여러 개 있다면 반드시 섹션 인덱스 값을 이용해서 설정해야 합니다.테이블 뷰 객체는 데이터 소스(data source)와 델리게이트(delegate)가 필요합니다. 데이터 소스는 UITableViewDataSource 프로토콜을 구현해야 하고, 델리게이트는 UITableViewDelegate 프로토콜을 구현해야합니다. 데이터 소스는 테이블 뷰가 테이블을 만들 때 필요한 정보를 제공하고 테이블의 행이 추가, 삭제 또는 재정렬할 때 데이터 모델을 관리합니다. 델리게이트는 화면에 보이는 모습과 행동을 담당합니다. 예를 들어 표시할 행의 수, 사용자가 특정 행을 터치했을 때, 행의 재정렬 등과 같은 것입니다.override func numberOfSections(in tableView: UITableView) -> Int {         // #warning Incomplete implementation, return the number of sections         return 1     }      override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {         // #warning Incomplete implementation, return the number of rows         return attractions.count     } 위의 두 소스는 데이터 소스가 필수적으로 구현해야 하는 메소드입니다. 하나는 섹션의 개수를 리턴하고, 또 하나는 한 섹션 안에 있는 행의 개수를 리턴합니다.테이블 뷰는 수정 모드에서 행을 추가, 삭제, 재정렬할 수 있습니다. 각 행은 테이블 뷰 셀에 연관된 editingStyle에 따라서 추가, 삭제, 재정렬을 할 수 있는데, 예를 들어 editingStyle이 insert라면 추가하는 메소드를 실행하고, delete면 삭제하는 메소드를 실행합니다. 행의 showsReorderControl 속성이 true라면, 재정렬하는 메소드를 실행할 수 있습니다.// Override to support editing the table view.     override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {         if editingStyle == .delete {             // Delete the row from the data source             ...                 // delete rows and attractions and reload datas             attractions.remove(at: indexPath.row)             tableView.deleteRows(at: [indexPath], with: .middle)             tableView.reloadData()         } else if editingStyle == .insert {             // Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view         }     } 위 소스는 editingStyle이 delete일 때 셀을 삭제하고 테이블 뷰를 다시 로드하는 기능을 구현한 것입니다.테이블 뷰를 만드는 가장 쉽고 권장하는 방법은 바로 스토리보드에서 테이블뷰컨트롤러(UITableViewController)를 이용해서 만드는 겁니다. 런타임에 테이블뷰컨트롤러는 테이블 뷰를 만들고 델리게이트와 데이터 소스를 자기 자신으로 할당합니다.컬렉션 뷰(UICollectionView)컬렉션 뷰는 테이블 뷰에서 할 수 있는 모든 것을 할 수 있습니다. 섹션을 가질 수 있고, 인덱스패스 값을 이용해서 셀을 구별합니다. 이 셀들은 컬렉션 뷰 셀(UICollectionViewCell)의 서브 클래스이며 데이터 소스(UICollectionViewDataSource)와 델리게이트(UICollectionViewDelegate)가 필요합니다. 셀을 추가, 삭제, 재정렬하는 기능도 구현할 수 있습니다. 그렇다면 컬렉션 뷰와 테이블 뷰를 구분하는 특징은 무엇일까요? 바로 레이아웃입니다. 컬렉션 뷰는 여러 개의 열과 행으로 셀을 표현할 수 있습니다. 예를 들어, 그리드(grid) 형태로 아이템의 목록을 보여줄 수 있습니다. 그래서 수직 스크롤뿐만 아니라 수평 스크롤도 할 수 있습니다.스토리보드에서 디자인한 테이블 뷰 셀과 컬렉션 뷰 셀위 스크린샷에서 테이블 뷰와 컬렉션 뷰의 가장 큰 차이는 바로 셀입니다. 테이블 뷰에서는 하나의 열에 여러 행을 표시하는 형식이기 때문에, 셀의 모습을 행에 맞춰서 디자인합니다. 하지만 컬렉션 뷰는 열과 행을 만들 수 있기 때문에, 꼭 행의 모습이 아니더라도 다양한 모습으로 셀을 디자인할 수 있습니다. 컬렉션 뷰 셀의 가장 큰 특징이기도 하죠. 위처럼 셀을 디자인하고 앱을 실행하면 아래의 화면이 나타납니다.테이블 뷰와 컬렉션 뷰의 앱 화면 차이또한 컬렉션 뷰는 레이아웃 객체가 있습니다. 기존에 제공하는 flow layout을 사용해도 괜찮지만, 본인이 원하는 레이아웃 모양을 custom layout을 만들어서 사용합니다. 이를 담당하는 프로토콜은 UICollectionViewDelegateFlowLayout 입니다.func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {         let fullWidth = collectionView.frame.size.width - (self.CGFLOAT_INSET_WIDTH * 3) - (self.CGFLOAT_ITEMSPACING * 3)         let width = fullWidth/3         return CGSize(width: width, height: width + self.CGFLOAT_HEIGHT_ATTRACTIONCELL_DEFAULT)     }         func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {         return UIEdgeInsetsMake(self.CGFLOAT_LINESPACING_VERTICAL, self.CGFLOAT_INSET_WIDTH, self.CGFLOAT_LINESPACING_VERTICAL, self.CGFLOAT_INSET_WIDTH)     } 위 소스에서 collectionView(:layout:sizeForItemAt:) 메소드는 해당하는 셀의 사이즈를 설정하고, collectionView(:layout:insetForSectionAt:) 메소드는 섹션 안에 margin을 설정합니다.여러 모양의 셀을 이루어 하나의 뷰 화면을 구현할 수도 있습니다. 섹션마다 셀을 만들어 각각 다른 모습의 셀을 설정하고, 한 화면에 다양한 모습의 셀을 가진 뷰를 만드는 것입니다. 예를 들어, 헤더, 메뉴, 본문, 푸터 각각 셀을 만들어서 원하는 모양으로 만들고, 하나의 뷰 컨트롤러에 셀을 조합해서 한 화면에 나타나게 할 수 있습니다. 이 방법을 사용하면 자주 사용하는 셀을 재활용할 수 있습니다. 똑같은 헤더와 푸터 셀을 여러 번 만들지 않고 기존의 셀을 재활용하면 시간도 절약하고, 훨씬 깔끔한 소스를 만들 수 있을 겁니다.브랜디 앱 스크린샷 일부위의 스크린샷처럼 여러 화면에서 보여줘야 할 똑같은 뷰가 있을 때, 셀 xib 파일을 만들고 컬렉션 뷰에서 셀을 섹션별로 설정 및 사용하면 재활용하기 좋습니다.Conclusion지금까지 테이블 뷰와 컬렉션 뷰의 특징들을 살펴봤습니다. 한마디로 정리하면 테이블 뷰는 가장 간단한 목록을 만들 수 있습니다. 컬렉션 뷰는 다양한 모습의 목록으로 커스터마이징(Customizing)할 수 있습니다.그렇다면 우리는 어떤 것을 선택해야 할까요? 구현할 목록이 얼마나 복잡한지에 따라 선택은 달라집니다. 테이블 뷰는 간단하고 보편적인 목록을 만듭니다. 반면에 컬렉션 뷰는 특정한 모습의 목록을 만들 수 있습니다. 그래서 테이블 뷰는 목록이 간단하고 디자인 변경이 없을 때만 사용하길 권장합니다. 하지만 나중에 디자인이 바뀔 수도 있다면 컬렉션 뷰를 사용하는게 더 좋겠죠.Simple is the best! 간단하게 구현할 수 있는 건 테이블 뷰를 사용합시다. 테이블 뷰에서 구현하기 힘들다면 컬렉션 뷰를 이용해 개성 있는 목록을 마음껏 만들어봅시다!참고UITableView - UIKit | Apple Developer DocumentationUICollectionView - UIKit | Apple Developer Documentation 글김주희 사원 | R&D 개발1팀[email protected]브랜디, 오직 예쁜 옷만#브랜디 #개발문화 #개발팀 #업무환경 #인사이트 #경험공유
조회수 2312

개발자에게 필요한 좋은 개발도구들

안녕하세요. 크몽 개발팀 입니다~ 개발자는 무엇인가 개발하기 전에 준비해야될게 있습니다. 바로 개발도구들 과 자신에게 잘 맞는 셋팅이 필요하죠.그래서 이번에 개발환경을 셋팅하면서 알게 된 정보를 공유하기위해 이번 포스트를 작성하게 되었습니다.첫번째 개발도구는 'ampps' 입니다.  ampps는 개발에 있어서 필요한 다양한 개발도구들을 제공해주고 있는데요. 정석대로 하나씩 개발도구들을 설치하게 된다면 많은 시간을 투자해서 설치 및 셋팅을 해야하지만ampps는 한번의 설치만으로 Apache, MySQL, PHP, Python, MongoDB 등등 기본적인 셋팅을 통해 초보개발자이더라도 쉽고 편리하게 사용할 수 있다는점이 가장 큰 장점이라고 생각하고 있습니다.지원되는 운영체제는 Windows, Mac, Linux 모두 지원하기때문에 어느 운영체제는 지원이 안되는 불편함은 없겠네요.사이트 :http://www.ampps.com/ 두번째 개발도구는 'WebStorm' 입니다.  WebStorm은 비쥬얼스튜디오나 이클립스와 같은 통합 개발환경을 제공하고 있습니다.그리고 현재 자바스크립트 프로그래밍에서 절대적인 최고의 에디터로 개발자 사이에서 유명하고 많은 개발자들이 사용하여 개발하고 있습니다. WebStorm의 좋은점은 작성한 코드에서 에러가 있다면 JSHint가 에러부분 밑에 워드프로세서 철자법검사기처럼 빨간 줄로 에러를 표시해 주기때문에 개발자의 실수들을 바로 잡아줄 수 있어서 정말 좋습니다. 그러나 사용자는 30일 평가기간이 끝나면 추가비용을 지불해야 사용할 수 있는데요. 비용을 지불할 만큼 좋은 에디터인점은 변함이 없습니다.  사이트 : https://www.jetbrains.com/webstorm/  앞으로도 공유할 정보들이 생길때마다 크몽팀 블로그에 업데이트 할 예정입니다.포스트 내용에서 찾으시는 정보들을 찾으셨으면 좋겠고 크몽팀 개발자이야기에 많은 관심 부탁드립니다. :)이상 포스트를 마치겠습니다. #크몽 #개발팀 #인턴 #인턴생활 #경험공유
조회수 799

HBase Meetup - 비트윈에서 HBase를 사용하는 방법 - VCNC Engineering Blog

비트윈에서는 서비스 초기부터 HBase를 주요 데이터베이스로 사용하였으며 사용자 로그를 분석하는 데에도 HBase를 사용하고 있습니다. 지난 주 금요일(11월 15일)에 HBase를 만든 Michael Stack 씨가 한국을 방문하게 되어 ZDNet 송경석 팀장님의 주최 하에 HBase Meetup Seoul 모임을 가졌습니다. 그 자리에서 VCNC에서 비트윈을 운영하면서 HBase를 사용했던 경험들이나 HBase 트랜잭션 라이브러리인 Haeinsa에 대해 간단히 소개해 드리는 발표 기회를 가질 수 있었습니다. 이 글에서 발표한 내용에 대해 간단히 소개하고자 합니다.비트윈 서비스에 HBase를 사용하는 이유비트윈에서 가장 많이 사용되는 기능 중 하나가 채팅이며, 채팅은 상대적으로 복잡한 데이터 구조나 연산이 필요하지 않기 때문에 HBase 의 단순한 schema 구조가 큰 문제가 되지 않습니다. 특히 쓰기 연산이 다른 기능보다 많이 일어나기 때문에 높은 쓰기 연산 성능이 필요합니다. 그래서 메세징이 중심이 되는 서비스는 높은 확장성(Scalability)과 쓰기 성능을 가진 HBase가 유리하며 비슷한 이유로 라인이나 페이스북 메신저에서도 HBase를 사용하는 것이라고 짐작할 수 있습니다.로그 분석에도 HBase를 사용합니다비트윈은 사용자 로그 분석을 통해서 좀 더 나은 비트윈이 되기 위해서 노력하고 있습니다. 비트윈 사용자가 남기는 로그의 양이 하루에 3억건이 넘기 때문에 RDBMS에 저장하여 쿼리로 분석하기는 힘듭니다. 그래서 로그 분석을 위해 분산 데이터 처리 프레임워크인 Hadoop MapReduce를 이용하며 로그들은 MapReduce와 호환성이 좋은 HBase에 저장하고 있습니다. 또한 이렇게 MapReduce 작업들을 통해 정제된 분석 결과를 MySQL에 저장한 후에 다양한 쿼리와 시각화 도구들로 custom dashboard를 만들어 운영하고 있습니다. 이를 바탕으로 저희 Biz development팀(사업개발팀)이나 Data-driven팀(데이터 분석팀)이 손쉽게 insight를 얻어낼 수 있도록 돕고 있습니다.HBase를 사용하면서 삽질 했던 경험HBase를 사용하면서 처음에는 잘못 사용하고 있었던 점이 많았고 차근차근 고쳐나갔습니다. Region Split과 Major Compaction을 수동으로 직접 하는 등 다양한 최적화를 통해 처음보다 훨씬 잘 쓰고 있습니다. HBase 설정 최적화에 대한 이야기는 이전에 올렸던 블로그 글에서도 간단히 소개한 적이 있으니 확인해보시기 바랍니다.HBase 트랜잭션 라이브러리 해인사Haeinsa는 HBase에서 Multi-Row 트랜잭션을 제공하기 위한 라이브러리입니다. 오픈소스로 공개되어 있으며 Deview에서도 발표를 했었습니다. HBase에 아무런 변형도 가하지 않았기 때문에 기존에 사용하던 HBase 클러스터에 쉽게 적용할 수 있습니다. 비트윈에 실제로 적용되어 하루 3억 건 이상의 트랜잭션을 처리하고 있으며 다른 많은 NoSQL 기반 트랜잭션 라이브러리보다 높은 확장성과 좋은 성능을 가지고 있습니다.발표에서 사용했던 슬라이드를 첨부하였으니 도움이 되었으면 합니다.<iframe class="speakerdeck-iframe" frameborder="0" src="//speakerdeck.com/player/2b8092b02ff90131ef414aa7d272d735?" allowfullscreen="true" mozallowfullscreen="true" webkitallowfullscreen="true" style="border: 0px; background: padding-box rgba(0, 0, 0, 0.1); margin: 0px; padding: 0px; border-radius: 6px; box-shadow: rgba(0, 0, 0, 0.2) 0px 5px 40px; width: 750px; height: 563px;">
조회수 2339

AWS S3를 이용하여 Vue 배포하기

Vue를 처음 만났을 때, 이것으로 무엇을 할 수 있을지 궁금했다. 하지만 Vue로 데모 앱과 개발 가이드를 따라하면서 의문은 점점 풀렸다. 알다시피 Vue는 front-end 로 활용이 된다. 빌드가 없어도 되고, 빌드를 해서 배포할 수도 있다. Vue는 일반 CDN을 이용하여 페이지를 만드는 방법과 여러 프레임워크를 활용하여 배포하는 방법 외에 다양한 방법이 존재하는데, 무슨 방법을 쓰든 결과물은 html과 js, css 같은 static 파일로만 이루어져 있다.처음에는 일반적인 방법으로 테스트하면서 다양한 디렉티브와 손쉽게 DOM 처리를 하는 방법을 익혔다. 나중엔 프로젝트에 참여하면서 webpack 으로 빌드해 배포하도록 프로젝트를 구성했다. webpack을 이용한 배포방법은 여기 를 참고하면 된다. 참고로 webpack은 nodeJS로 실행되기 때문에 기본적인 환경을 세팅해야 한다.webpack build.js 일부위처럼 직접 스크립트를 만들어서 사용해도 되지만 Vue에서 제공하는 템플릿으로 프로젝트를 생성할 수도 있다. 단 Vue-CLI가 미리 설치되어 있어야 한다.터미널에서 vue init webpack 프로젝트명만 치면 세팅된 템플릿으로 폴더 및 스크립트들이 구성된다. 아래와 같이 프로젝트의 기본 속성들을 입력하자.프로젝트를 만들면 기본적인 파일들로 이루어진 폴더가 생성된다. 현재는 관련 라이브러리들이 없는 상태이므로 npm install 을 통해 설치한다. 설치 후 nom run dev 로 개발모드를 실행하면 브라우저로 화면을 볼 수 있다. 만약 설치하고 빌드 설정을 수정하지 않았다면 기본 8080 포트로 가동된다. 브라우저를 실행해 http://localhost:8080 으로 접속하면 아래와 같은 화면이 나온다.여기까지 하면 webpack 으로 배포할 수 있는 상태가 되었다. 이제 AWS로 가서 회원가입을 하고 S3를 생성한다. 생성 방법은 여기를 참고하면 된다. 버킷까지 생성되었다면 이제 빌드 후 업로드하자.위와 같이 nom run build 를 하면 빌드가 시작된다.빌드가 완료되면 해당 프로젝트 폴더에 dist 폴더가 생성된다. dist 폴더에는 index.html 과 js, css 와 같은 리소스들이 들어간다. 이제 S3로 가서 올리려는 버킷을 클릭하자.업로드 버튼을 클릭하고, dist 폴더에 있는 index.html 과 static 폴더를 업로드한다. 폴더가 업로드되면 아래와 같이 파일과 폴더들이 보인다.업로드가 완료되었다고 지금 바로 웹사이트처럼 접근할 수는 없다. 정적 웹사이트 호스팅 설정을 활성화해야 비로소 가능하다. 속성 탭을 클릭해 정적 웹사이트 호스팅을 활성화 상태로 만든다.위와 같이 활성화하고 인덱스 문서에만 index.html 을 입력한 후 저장 버튼을 클릭한다. 현재 보이는 엔드포인트 주소가 외부에서 접근할 수 있는 사이트 도메인이다. 그 후 엔드포인트 주소로 접속하면 아래와 같이 오류 페이지를 볼 수 있다.이게 무슨 오류란 말인가… index.html 파일도 있는데 403 오류라니..자세한 http 응답코드는 여기를 참고하면 된다. 위의 오류는 권한이 없어서 파일에 액세스할 수 없다는 페이지다. S3는 기본적으로 모두에게 공개하진 않는다. 그래서 특정 파일이나 특정 버킷만 공개형으로 변경해줘야 한다.이 문제를 해결하려면 권한 탭으로 이동해 버킷 정책을 설정해야 한다. 아래와 같이 설정해주면 누구에게나 공개되어 접근할 수 있다.위 내용을 아래와 같이 버킷 정책으로 설정한다.설정을 저장한 후 다시 엔드포인트로 접속하면 아래와 같이 로컬에서 보였던 페이지가 보인다.이렇게 보이면 성공!다음엔 Vue가 어떤식으로 동작을 하는지 알아보도록 하겠다.마치며Vue는 간결하면서도 강력한 기능을 가지고 있는 front-end 프레임워크다. 개념과 디렉티브, 이벤트 핸들링, 보안 등 궁금한 게 많았지만 신통방통한 놈인 건 확실하다. 아직 큰 프로젝트에 사용하는 건 힘들 수도 있으나 아래와 같이 장점이 많아 서버단과 클라이언트단 분리 개발, 외부 라이브러리와 사용하면 훌륭한 프레임워크가 될 거라는 생각이 든다.재사용 가능한 기능별 컴포넌트 개발훌륭한 라우터 탑재서버와 통신 가능한 ajax 모듈이 다양함 ( jQuery Ajax, Axios )다양한 호환 라이브러리를 활용하면 분명 훌륭한 프레임워크가 될 것!편집자 주) 함께 보면 좋아요!Vue, 어디까지 설치해봤니?PHP Codeigniter 환경에서 VUE 사용해보기JQuery 프로젝트에 VUE를 점진적으로 도입하기Vue와 Vuex, 컴포넌트간 통신과 상태 관리글장현준 팀장 | R&D 개발3팀[email protected]브랜디, 오직 예쁜 옷만
조회수 3045

JANDI CONNECT 개발기

지난 1월 말, 새해를 맞아 잔디에 새로운 기능이 업데이트되었습니다. 바로 잔디 커넥트에 관한 내용인데요, 협업에서 많이 쓰이는 몇 가지 외부 서비스를 잔디와 쉽게 연동해서 더욱 효율적인 업무 커뮤니케이션을 할 수 있게 되었습니다. 많은 고객분들이 이번 업데이트를 기다려주신 만큼, 저희 개발팀 또한 기대에 보답하고자 지난 몇 주의 스프린트 동안 열심히 준비했습니다. 이번 글에서는 커넥트 동작 방식을 설명하고 그 개발 과정에서 저희가 겪은 시행착오를 비롯한 여러 값진 경험들을 공유하고자 합니다.Integration? Webhook!연동: [기계] 기계나 장치 따위에서, 한 부분이 움직이면 다른 부분도 함께 잇따라 움직임.앞서 말한 대로 잔디 커넥트는 여러 웹 서비스들과 잔디를 연동할 수 있는 기능입니다. 서로 다른 웹 서비스를 연동하기 위해선 한 서비스 내에서 특정 이벤트가 발생 했을 때 다른 서비스로 해당 이벤트를 알려주는 연결 고리가 필요합니다. 이때 해당 연결 고리 역할을 위해 대표적으로 사용되는 기법이 웹훅(WebHook) 입니다. 웹훅은 user-defined HTTP callbacks, reverse APIs 등으로 불리는데, 간단히 설명하자면 웹 서비스에서 공개한 API가 아닌 사용자가 직접 지정한 주소(URL)로 특정 이벤트가 발생 시 HTTP Request를 보내주는 기법입니다. 예를 들어,새로운 일정이 등록된 경우(Google Calender)요청한 Pull Request가 Merge된 경우(GitHub)카드에 새로운 코멘트가 작성된 경우(Trello)이러한 이벤트가 발생했을 때 사용자가 매번 이벤트가 발생했는지 확인하지 않아도 서비스가 먼저 알려줄 수 있도록 일종의 알림을 등록하는 것이죠. 잔디 커넥트는 이와 같은 특징을 이용해서 각각의 웹 서비스에서 제공하는 웹훅을 잔디의 메시지 형태로 전달하는 기능입니다.일반적으로 웹훅은 이벤트에 대한 알림을 외부로 전달하는 것을 말합니다. 이 부분에서 중요한 것은 전달 방향인데, 서비스 내부에서 외부로 전달하기 때문에 이를 Outgoing Webhook으로 부르기도 합니다1. 같은 맥락에서 반대로 생각해보면 외부에서 서비스 내부로 특정 데이터를 전달하는 경우이니 Incoming Webhook이 됩니다. 앞서 웹훅을 reverse API라고 했는데 이를 다시 뒤집으니 결국 서비스 내부로 통신하는 제한적인 API와 같은 역할을 합니다. 굳이 용어를 구분한 이유는 API와 달리 접근하려는 서비스의 별도 인증 절차를 거치지 않고도 사용자가 생성한 웹훅의 URL을 인증 토큰으로 사용하며 약속된 Request Body 포맷만 알고 있다면 자유롭게 사용할 수 있기 때문입니다.개념 설명이 다소 길어졌지만, 이번 잔디 커넥트 기능에 대해 용어나 개념이 낯설다는 피드백이 생각보다 많았기 때문에 이번 글을 통해 더 많은 분들이 웹훅을 이해하는 데 도움이 될 수 있으면 좋겠습니다.구현에 앞서서비스를 운영한지 1년 정도 지난 시점에서 저희 내부적으로는 백엔드의 기술 스택 변경 및 각 서비스 분리에 대한 갈증이 있었습니다. 하지만 이미 서비스를 운영 중이기 때문에 안정성이 최우선시 되는 만큼 꽤 부담스러운 숙제로 미뤄둘 수밖에 없었고요. 때마침 커넥트 기능은 숙제를 시험해볼 만한 좋은 기회임에는 분명했지만, 새로운 기술 스택을 바로 서비스에 적용하기엔 오히려 개발 효율이 떨어질 것이라는 판단하에 일단 서비스 분리에만 집중하기로 했습니다.기본적으로 API와 DB를 기존 서버와 분리하고 웹훅 데이터를 저장하기 위한 큐와 해당 데이터를 처리하는 배치 서버 또한 모두 기존 서비스와 분리해서 최대한 결합도를 제거했습니다. 이런 설계 덕분에 추후 사업 전략이나 각 국가의 특성에 맞춰 커넥트 기능을 어렵지 않게 포함하거나 제외할 수 있게 되었습니다. 전반적인 저희 잔디 백엔드 아키텍쳐에 대해서는 아직 한 번도 소개 해드린 적이 없으니 다음에 따로 주제로 선정해 집중적으로 다뤄보도록 하겠습니다.동작 방식잔디 커넥트가 동작하는 방식은 기본적으로 다음과 같습니다.Incoming Webhook URL 생성 - 외부 서비스 웹훅 등록 - 웹훅 수신 - 메시지 작성 연동 대상 서비스마다 조금씩 차이가 있지만, 기본적으로 모두 위와 같은 방식으로 동작하기 때문에 단계마다 나누어 설명하겠습니다.1. Webhook URL 생성Webhook URL은 https://wh.jandi.com/connect-api/webhook/{teamId}/{webhook-token}와 같은 형태로 생성됩니다. hostname을 별도로 설정함으로써 기존 API 서버와의 분리는 물론이고, nginx의 Limiting the Request Rate 설정을 이용해서 호출되는 웹훅 요청 수를 효과적으로 제한할 수 있었습니다. webhook-token은 중복을 피하면서 각 웹훅에 대한 유효성을 검증할 수 있도록 여러 키를 조합한 md5 hash 값을 이용했습니다.이렇게 생성된 URL은 Incoming Webhook 뿐만 아니라 Google Calendar 등의 서비스에 등록하는 콜백 URL로 사용합니다.2. 외부 서비스 웹훅 등록웹훅을 등록하는 방법은 서비스에 따라 API를 이용하거나 수동으로 직접 등록할 수 있습니다. 사용자가 직접 웹훅을 등록하는 방법은 웹훅 URL만 생성해서 전달하면 등록 과정의 추가 처리가 필요 없어서 간단하지만, 서비스마다 등록하는 방법이 조금씩 다르고 다소 복잡하게 느껴지는 문제가 있습니다. 반대로 각 서비스에서 제공하는 API를 이용해 웹훅을 등록하면 사용자의 부담을 많이 줄일 수 있지만, 그만큼 내부적으로 처리해야 할 작업이 많아집니다. 그래서 구현 초기에 꽤 많은 시간을 투자할 수밖에 없었고 그 과정에서 아래와 같은 어려움을 겪었습니다.웹훅 관련 API를 사용하려면 먼저 인증을 받아야 하는데 서비스마다 제공하는 인증 방식이 조금씩 달라서 이를 통합하는 모델을 만들기가 쉽지 않았습니다. 요약하자면 기본적으로 accessToken을 사용하지만, 인증 방식에 따라 부가적으로 필요한 데이터가 서로 조금씩 다른것이죠. 가령, 구글캘린더는 만료 일시와 토큰 갱신을 위한 refreshToken 값을 별도로 갖고 있어야 합니다. 또 한가지 놓치기 쉬운 부분은 인증 폐기(revoked) 관련한 데이터 처리인데 저희가 경험한 바로는 인증이 폐기되었을 때 별도로 웹훅 알림을 주지 않기 때문에 반드시 인증의 유효성을 확인하는 추가 로직이 필요합니다.대부분의 사무실이 그렇듯이 저희 또한 공유기를 이용해 내부 네트워크를 구성하고 있습니다. 게다가 백엔드 파트는 개개인의 로컬 가상 서버에 동일한 환경을 설정해놓고 개발을 하므로2보통 경우엔 외부(public network)에서 들어오는 요청을 받을 수 없습니다. 그렇다고 매번 외부 네트워크에 있는 서버에 배포 후 테스트하기가 어려우니, 저희는 각 로컬 서버마다 고유 포트 번호를 나눠 갖고 WAN이 물린 공유기의 포트 포워딩을 알맞게 설정한 뒤에 네트워크 터널링 유틸리티인 ngrok을 이용해 내부와 연결되는 public 주소를 생성해서 외부 서비스와 문제없이 통신할 수 있었습니다.3. 웹훅 수신웹훅을 통해 들어오는 Request는 일단 정상 응답을 하는 게 좋습니다. 서비스마다 최초 웹훅 등록 시 유효한 URL인지 확인하는 테스트 요청을 하는데 이때 정상 응답을 하지 못하면 아예 등록조차 처리되지 않습니다. 또한, 정상적으로 등록된 이후 특정 이벤트에 해당하는 웹훅 요청에 대한 응답에도 주의할 필요가 있는데, 만약 에러 응답이 반복되면 일정 시간 동안 각 서비스에서 아예 해당 웹훅을 발송하지 않도록 제한이 걸려 더 이상 테스트를 진행할 수 없는 경우도 있었습니다.따라서 일단 웹훅 요청이 들어오면 teamId와 webhook-token 값으로 올바른 웹훅인지 검증한 후 서비스별 큐에 Request header와 body를 포함한 데이터를 전달한 뒤 바로 응답하고, 큐에 쌓인 데이터는 커넥트 종류별로 배치 서버가 돌면서 처리하게 됩니다. SQS를 사용함으로써 늘어나는 데이터에 대한 안정성을 확보하고 각각의 배치 서버를 독립적으로 분리해서 구현함으로써 자연스레 확장성(scalability)도 보장할 수 있게 되었습니다.4. 메시지 작성웹훅 데이터를 잔디의 메시지로 변환하는 역할은 배치 서버가 담당합니다. 서비스별로 데이터 포맷이 다르므로 해당 데이터를 파싱 및 처리하는 Worker 또한 각각 구현했습니다. 사실 커넥트 기능에서 가장 핵심적인 역할을 하는 부분인 만큼 가장 많은 공수가 드는 작업이였던 것 같습니다.서비스마다 정해놓은 웹훅 이벤트와 잔디 커넥트에서 제공하고자 하는 알림이 서로 완전히 일치하지 않아서 이를 서로 연결하는 작업연동 서비스의 문서가 잘 정리되어 있지 않아서 일일이 필요한 동작을 취하고 그에 따라 들어오는 데이터를 정리하는 작업잔디 계정 언어에 따라 메시지 L10N3을 적용하는 작업커넥트 메시지를 전달하기 위해 기존 멤버와 다른 커넥트 봇을 구현하는 작업등 요약하기 어려울 정도로 크고 작은 이슈들이 많았습니다. 그 내용이 너무 다양해서 모두 상세히 기록하긴 어렵지만, 개중에 도움이 될만한 내용을 추려서 아래 따로 정리했으니 관심 있으신 분들은 참고하시면 좋을 것 같습니다.서비스별 집중 탐구커넥트 구현 일정을 최대한 앞당기기 위해 저희는 개발자들끼리 각각의 커넥트 종류 별로 전담해서 작업하는 전략을 취했습니다. 제가 대표로 글을 작성하기는 하지만 보다 정확하고 구체적인 정보를 전달하는 것이 좋겠다는 생각에 개발을 담당하신 분들과의 짧은 인터뷰 형식을 빌려 공유하겠습니다.- Google CalendarQ. 기술적으로 난이도가 높았던 작업을 소개해달라.전반적으로 어려운 작업이 있었다기보단, 캘린더 특성상 세세하게 처리할 부분들이 많아 설계와 구현이 어쩔 수 없이 복잡해졌다. 가장 골치 아팠던 작업은 일정 알림을 타임존(Time Zone)에 따라 각각 알맞은 시간에 전달하는 작업인데, “잔디 계정의 타임존”, “구글 캘린더의 타임존”, “개별 일정의 타임존” 이렇게 3가지를 모두 고려해서 경우마다 기준이 되는 타임존을 결정하는게 엄청 까다로웠다. 심지어 구현 후 테스트를 하는 과정에서도 출력된 시간이 올바로 표시된 것인지조차 헷갈려서 디버깅하는데 한참 고생할 수 밖에 없었다.웹훅을 등록하고 관리하는 부분도 꽤 복잡했는데, 구글 답게(?) 웹훅에도 만료 기간이 존재한다는 것이 포인트다. 때문에 만료되기 전에 반드시 재등록 및 과거 웹훅 삭제 작업을 하는데, 효과적으로 처리하기 위해 “웹훅을 받을 때마다 만료 기간을 확인”, “등록된 일정이 많지 않아 웹훅을 받지 못하는 경우도 있으니 별도의 배치서버가 하루 단위로 확인” 이렇게 두 가지 로직을 넣어서 자동으로 웹훅을 유지하도록 구현했다.또한, 다른 연동 서비스와 달리 구글은 웹훅 콜백으로 들어오는 요청에 해당 이벤트에 대한 데이터를 직접 담아주지 않기 때문에 key를 가지고 한 번 더 API 호출을 통해 필요한 데이터를 가져와야 한다는 점도 주의해야 한다. 요청해야 할 API 문서는 비교적 잘 정리된 편이지만, 같은 요청에 대해서도 인자를 어떻게 보내는지에 따라 그 응답이 제각각이기 때문에 응답 값에 대해 무조건 신뢰하고 처리해서는 안 된다. 당연히 존재할 것으로 생각한 필드 값에 빈 배열이 들어와서 일정 관련된 데이터를 일부 날리고 나서야 깨달았다.. -_-Q. 가장 처리해야 할 이슈가 많았다고 알고 있는데, 그중에서도 기억에 남는 이슈가 있을 것 같다.너무 많은 이슈를 동시에 처리하다 보니 특별히 기억에 남는 이슈는 없다. 다만 아직도 왜 그랬는지 확실한 이유는 알 수 없지만, 언젠가 한 번 구글에서 웹훅을 아예 전달해주지 않았던 경우가 있었다. 과도한 요청으로 limit이 걸린 것도 아니었는데, 갑자기 웹훅이 안들어오니깐 우리로서는 어떻게 풀어볼 방법이 없었다. 그러다 나중에 확인해보니 대략 12시간쯤 지나고 나서 그동안 밀려있던 웹훅 데이터가 한 번에 밀려서 들어와 있더라. 다행히 그 이후로 지금까지 한 번도 재현되지 않는걸 보니, 혹 동일한 증상을 겪는다면 당황하지 말고 기다려 보시라.반복 일정을 다루는 것도 꽤 골치 아픈 이슈인데, 왜냐하면 일정이 있을 때 마다 웹훅 알림을 주지 않고 처음 등록된 시점에서 한 번만 정보를 알려주기 때문에 등록된 시점 이후의 일정은 내부적으로 계속 등록해줘야 한다. 기본적으로 구글 캘린더는 RFC-55454 표준을 따르지만, 실제 전달되는 데이터 중 일부는 표준과 조금 다른 부분이 있었다. 특히 반복 일정(recurrence) 관련 데이터 포맷이 조금 다르므로 캘린더 데이터를 파싱하기 위해 만약 외부 library를 사용한다면 별도의 예외처리가 필요하다. 더욱 더 까다로운 건 사실 등록된 반복 일정이 수정되거나 삭제되는 경우인데, 이때 “특정 일정만 삭제”, “지금 시점 이후의 일정 모두 수정” 등 워낙 케이스도 많고 각각을 테스트 하는 것도 쉽지 않기 때문에 작업 시간이 꽤 오래 걸렸다. (심지어 아직 확인하지 못한 드문 케이스에서는 잠재된 버그가 있을 수도…)Q. 그 밖의 도움이 될만한 노하우나 꿀팁이 있다면?구글 캘린더 API는 Webhook 보단 Push Notification 키워드를 많이 사용한다. 푸시 노티라는 게 좀 다른 카테고리에서 많이 쓰이는 용어이기도 하다 보니 코드 리뷰 등의 커뮤니케이션을 할 때 혼동이 좀 있었던 것 같다.물론 서비스 요구사항마다 다르겠지만, 잔디 같은 경우엔 요구사항에 맞춰 계속 설계를 변경 및 개선하다 보니 결과적으로 너무 복잡해져 효율이 떨어지는 코드를 작성할 수밖에 없었다. 처음부터 연동을 생각하기보다는 아예 캘린더 자체 기능을 베이스로 설계하고 데이터만 구글에서 가져온다 생각했다면 개발 생산성이 더욱 좋았을 것 같다.- TrelloQ. 기능을 구현하면서 느낀 아쉬웠던 점과 좋았던 점을 짚어달라.트렐로 공식 API 문서가 더 명확했다면 좀 더 개발이 수월했을 것이다. 문서가 RESTful하게 end-point path는 간결하게 잘 정돈되어 있지만, 각 요청 parameter에 대한 설명이나 response 데이터 등이 명확하게 정리되지 않아서 적합한 API를 찾거나 불명확함을 걷어내기 위한 테스트를 하다 보니 전반적으로 시간이 길어지고 비효율적이었던것 같다.그에 반해 트렐로에서 웹훅 이벤트를 발생시키기 위한 유저 액션들이 비교적 간단하고, 그에 따른 콜백 리퀘스트 또한 누락 없이 빠르게 잘 들어와서 그나마 쉽게 테스트를 할 수 있었다.Q. 기능 구현을 위해선 반드시 알아야 할 웹훅 이벤트 종류 및 데이터에 대한 문서는 정리가 전혀 안 되어있다고 하던데 정말인가?그렇다. 처음엔 좀 당황했지만, 그래도 방법이 없으니 일일이 경우마다 테스트해보면서 직접 정리를 하려고 했다. 하지만 각 웹훅마다 큰 구분만 있고 세세한 데이터는 너무 다양해서 깔끔하게 정리하기가 어려워 따로 공유를 위한 문서를 만들지는 못했다. 예를 들자면 트렐로에서 updateCard 라는 action type의 웹훅 데이터를 보내주는데, 그 데이터만 보고 “Card Archive”, “Description 수정/삭제”, “Due date 등록/수정”, “카드 이동” 등의 여러 가지 서로 다른 이벤트를 구분해야 한다. 근데 그 구분하는 방법이 특정 flag가 있는 게 아니라서 각 data를 모아놓고 역으로 분리하다 보니 코드를 깔끔하게 작성하기가 어려움은 물론, 추후 트렐로 측 데이터의 변동이 있을 때의 품질을 보장할 수 없는 리스크를 안고 구현할 수밖에 없었다.Q. 그 밖의 도움이 될만한 노하우나 꿀팁이 있다면?만약 트렐로와 어떤 형태로든 연동하려고 한다면, 설계 전에 모든 API에 대해 꼼꼼히 살펴보고 웹훅 이벤트 또한 직접 테스트해서 일단 전체적으로 리스트업을 정리하는 게 보다 생산성에 도움이 될 것이다. 트렐로를 잘 알고 있더라도 서비스 내부에서 “보드”, “리스트”, “카드”가 어떤 상관관계를 가지는지 미리 정리해보는 것도 좋다.사소하지만 좀 특이했던 점은 웹훅을 처음 등록할 때 해당 URL로 확인 요청을 한번 하는데, 이때 요청은 HTTP method가 POST가 아닌 HEAD로 들어온다. 그래서 반드시 동일한 URL의 HEAD 요청에 대해서도 정상 응답을 할 수 있도록 구현해야 한다.마무리잔디 커넥트를 구현하면서 특히 서비스 품질과 개발 속도 간의 밸런스에 대한 고민을 많이 했습니다. 초반에 서비스 종류별로 작업을 분리하고 각각의 방식으로 설계한 뒤 나중에 정리하는 전략이다 보니 공통으로 가져갈 수 있는 DB 모델이나 서비스 로직이 많아서 이를 통합하기 위해 반복 작업을 할 수밖에 없었는데 이 부분이 저희 내부적으로 느낀 가장 아쉬운 부분이 아니었나 생각합니다. 기능 중 많은 부분이 외부 서비스에 의존적이다 보니 생각하지도 못한 크고 작은 이슈들이 발생해서 일정 산출에도 꽤 어려움을 겪었습니다.커넥트 기능을 출시한 이후로 꽤 시간이 지났음에도 불구하고 이슈 백로그(Backlog)를 보니 아직도 개선할 부분이 많이 남아있는 듯 합니다. 그렇지만 이번에 기반이 되는 작업을 최대한 튼튼히 하기 위한 많은 시행착오를 거쳤기에, 추후 연동되는 커넥트 종류를 늘려나가는 시점5에 보다 효과적으로 개발할 수 있을 것이라 기대하면서 이번 글을 마치겠습니다.Slack API 문서 참고 ↩vagrant의 box로 서로의 로컬 개발 환경을 동일하게 유지하고 있습니다. 참고로, 현재 저희 서버 환경은 Local - Dev - Staging - Production으로 구성되어 단계별로 상황에 알맞게 배포하고 있습니다. ↩Localization의 약어. 잔디는 아시아 시장에 최적화된 서비스를 제공하고자 한국어, 일본어, 중국어 간체자(중국), 번체자(대만/홍콩), 영어 총 5가지 언어를 지원합니다. ↩아이캘린더(iCalendar)로 불리는 인터넷 캘린더의 데이터 포맷에 관한 표준. IETF 문서참고 ↩구체적인 시점은 말씀드리기 어렵지만, 더욱 좋은 사용성을 제공하고자 유저분들의 설문조사를 진행하고 있으니 많은 참여 부탁드립니다. ↩#토스랩 #잔디 #JANDI #개발후기 #일지 #인사이트
조회수 4266

왜 SVG로 갈아탔는가?

이 글에서는 데일리호텔이 왜 png에서 svg로 갈아탔는지, 그리고 간단한 svg 실무 적용 팁에 대해 알려드리고자 합니다.01 SVG란 무엇인가?SVG는 “ Scalable Vector Graphics”의 약자입니다.JPEG, PNG 처럼 SVG도 그래픽 포맷(Graphic format) 중 하나입니다. SVG는 벡터 기반이기 때문에 리사이징이 되어도 전혀 깨지지 않습니다. 모든 해상도에서 자유자재로 활용할 수 있기 때문에 특정 해상도에 제한되어있지 않다는 게 핵심 포인트라고 할 수 있습니다.02 SVG가 왜 좋은가?다른 그래픽 포맷보다 SVG가 좋은 이유는 참으로도 다양합니다. 필자가 생각했을 때의 핵심 장점들은 이러합니다.1. 특정 사이즈에 구애를 받지 않습니다.즉 어느 해상도에서든 pixelate 되지 않습니다. 요새 디자이너들이 자주 사용하는 디자인 프로그램인 스케치로 따지면 아트보드와 비슷한 것 같습니다. 아트보드 안에 만든 레이어, 요소들은 다 벡터 기반입니다. 아트보드를 리사이징 해도 안에 요소들은 깨지지 않고 그 모습 그대로를 가지고 있습니다. 같은 원리로 SVG도 어떤 사이즈로든 그 모습 그대로가 유지됩니다. 그렇기 때문에 사이즈별로 아이콘을 일일이 생성해서 개발자에게 넘겨줄 필요가 없습니다. SVG 파일 하나면 모든 해상도를 대응할 수 있습니다.2. 작은 파일 사이즈비트맵 이미지들(PNG, JPEG) 같은 경우 파일 크기를 결정하는 주요 요소는 바로 ‘해상도’입니다. 예를 들어 5000x5000 픽셀 이미지는 항상 500x500보다 파일 사이즈가 큽니다.반면, SVG 그래픽 같은 경우 파일 크기를 결정하는 주요 요소는 바로 ‘복잡도’입니다. Path가 비교적 적은 간단한 이미지는 PNG, JPEG 보다 파일 사이즈가 적을 수도 있지만 이미지를 구성하는 요소의 복잡도(레이어가 많다든지 특정 효과가 많다든지)에 따라 파일 사이즈가 커집니다.하지만 이런 용량 문제는 SVG Optimizing을 하게 되면 나름 해결됩니다. 필자 같은 경우 업무적으로 스케치를 사용하고 있기 때문에 스케치에서 제공해주는 SVGO Compressor 플러그인을 활용하고 있습니다.https://github.com/BohemianCoding/svgo-compressorBohemianCoding/svgo-compressorsvgo-compressor - A Plugin that compresses SVG assets using SVGO, right when you export them. This Plugin requires Sketch 3.8.github.com 작은 파일 사이즈로 인해 로딩 시간도 훨씬 더 줄어든다는 장점 또한 있습니다.여기서 잠깐!혹시나 Bitmap과 SVG의 구성요소에 대해 잘 모르실 분들을 위하여 간단한 비교 해드리겠습니다.비트맵 그래픽: Raster Graphics (픽셀 기반)대표적인 포맷은 JPEG, PNG입니다. 이들은 픽셀로 구성되어 있습니다. 예를 들어 2x2 픽셀인 비트맵 이미지는 총 4px로 구성되어 있습니다. 개개인에 대한 픽셀들은 자유자재로 바꿀 수가 없고 움직일 수도 없습니다. 그렇기 때문에 100% 이상으로 이미지를 확대하면 Pixelate가 됩니다.SVG 그래픽: 벡터 기반픽셀로 구성되어 있지 않고 작업하고 있는 그래픽에 대한 정보로 구성되어 있습니다. 그렇기 때문에 어떤 사이즈로든 자유자재로 늘어나는 것이 가능합니다. 이러한 이유들로 인해 코드로 쉽게 적용된 스타일을 수정할 수 있습니다. 예를 들어 동그라미의 보더 값을 6에서 8로 바꾼다 / 색상을 그레이에서 블랙으로 바꾼다 / 사이즈를 40x40에서 80x80을 바꾼다 등스케치로 작업할 때도 쉽게 두 개의 차이점을 확인해볼 수 있습니다. 스케치에서 Export를 할 경우 비트맵 이미지는 하나의 압축된 레이어로 Export 됩니다. 반면 SVG는 레이어 그대로 눈에 보이지 않는 그래픽을 구성하는 정보들이 같이 저장된 채 Export가 됩니다.SVG를 구성하는 눈에 보이지 않는 정보들03 스케치가 SVG 이미지를 Export하는 방식다른 그래픽 포맷보다 SVG가 좋은 이유는 참으로도 다양합니다. 제가 생각했을 때의 핵심 장점들은 이러합니다.Sketch Export 기능스케치 하단 오른쪽 패널을 보면 Export 버튼이 있습니다. 여기서 Format을 SVG로 바꾸고 Export하면 금방 쉽게 끝나겠지 라고 생각할 수 있는데 여기서 조심해야 할 점은 본인이 어떻게 이미지를 작업했냐에 따라 옳지 않게 SVG가 내보내질 수 있습니다. 옳지 않게 SVG가 내보내 지게 되면 나중에 두 번 일을 작업하는 일이 발생할 수도 있습니다.쉽게 이해하실 수 있도록 이미지를 제작해 보았습니다. 아래 이미지는 같은 디자인인데 만들어진 방식이 각각 다릅니다.같은 아이콘이지만 구성하는 방식이 다름1. Two Shape2. One Shape3. Border and Shape Mix위 3가지 방법들은 옳고 그름이 없습니다. 다만 어떻게 이 아이콘을 나중에 활용할 것인가에 따라 만드는 방법이 달라지겠죠. 만약에 자동차 아이콘 안에 헤드라이트 색상을 바꾸고 싶다고 하면 위 방법 중 1번을 선택하면 될 것이고 선의 두께를 따로 조정하고 싶다 하면 3번 방식을 택하면 됩니다.SVG에 대해 잘 알지 못할 때는 프로그램 탓을 했었습니다. ‘왜 프로그램이 알아서 잘 못해주지?’라는 질문을 던졌지만… 슬프게도 이건 프로그램 잘못이 아닌 작업자 잘못입니다 �스케치 프로그램이든 아도비 일러스트레이터든 이 프로그램들은 디자이너가 만든 그래픽을 있는 그대로 svg 레이어로 번역하도록 프로그램이 되어 있습니다. 디자이너가 어떻게 작업했냐에 따라 그 정보 그대로 인식해서 svg로 만들어줍니다.04 SVG 아이콘이 제대로 적용 안될 경우다른 그래픽 포맷보다 SVG가 좋은 이유는 참으로도 다양합니다. 필자가 생각했을 때의 핵심 장점들은 이러합니다.헐 이건 도대체 왜….?!!!어느 날 SVG를 적용하기로 마음먹고 데일리호텔 앱 내 편의시설 아이콘 중 수영장 SVG 파일을 개발자에게 넘겼습니다. 근데 구멍이 뚫려야 할 곳이 채워져서 나오는데 원인을 모르고 헤매던 시절이 있었습니다. 미디엄에서 이 문제를 해결해줄 좋은 글을 발견하게 되었는데 난생처음 보는 단어가 2개 있었습니다.Even-Odd, Non-Zero…여기서 Even-Odd, Non-Zero의 차이점을 자세히 언급하기에는 너무 길어서 제가 참고한 미디엄 블로그 링크를 공유해드릴 테니 가서 보시면 이해하실 수 있을 것 같습니다. 작업하기에 앞서 꼭 읽어보시기를 권장합니다.https://medium.com/sketch-app-sources/preparing-and-exporting-svg-icons-in-sketch-1a3d65b239bbPreparing and Exporting SVG Icons in Sketch – Design + Sketch – MediumThis article is going to assume that you already understand the fundamentals of icon design. And focus on how to prepare and export them…medium.com 그래도 가볍게 필요한 내용만 공유드리자면 안드로이드에서는 fill-rule:evenodd를 제대로 지원하지 않고 fill-rule:nonzero만 지원한다고 보시면 됩니다. Even Odd는 특정 앱에서 호환이 안된다는 뜻입니다. (안드로이드 API 24 이상에서만 evenodd가 지원됨)근데 우리가 사용하고 있는 스케치 프로그램에서는 default값이 fill-rule:evenodd로 설정이 되어있고 여러 Path가 겹치는 아이콘 같은 경우 그대로 svg export를 하게 되면 위에서 제가 경험하였던 아이콘이 다 채워진 현상을 겪을 수 있게 되는 것입니다.1. Fills 섹션에서 Even-Odd를 Non-Zero로Fills 섹션에 가면 설정 아이콘이 있습니다. 클릭 시 Even-Odd가 디폴트 값인 것을 확인할 수 있습니다.스케치 Fill Default 값 = Even-OddNon-Zero로 설정값을 바꾸면 수영장 사다리 부분이 가득 채워진 채로 나오게 되는 것을 확인할 수 있습니다. 실제로 이 파일을 개발자에게 넘기게 되면 이렇게 채워진 채로 아이콘이 노출이 됩니다.Non-Zero 설정 / 모든 shape이 다 칠해짐이렇게 나가면 안 될 테니 수정하는 법을 알려드리겠습니다.2. Paths > Reverse Order 적용원래 뚫려 있어야 하는 Path를 Layer 패널에서 찾으면 됩니다. 빨간색으로 칠한 부분이 뚫려있어야 하는 부분들입니다.레이어 패널에서 path 확인하기Path가 선택된 채로 Layers > Paths > Reverse Order을 클릭합니다.Paths > Reverse OrderReverse Order을 클릭한 후 원래 뚫려있어야 하는 부분이 뚫리게 됩니다. 이 상태로 svg로 export하시고 개발자에게 전달을 하면 됩니다.마치며개인적으로 SVG에 대한 장점이 너무나도 크다고 생각하여 굳이 갈아타지 않을 이유가 없다고 생각합니다. 특히 Web 디자인을 할 때도 SVG를 저는 적극적으로 사용하시라고 권장하고 싶습니다. � 안드로이드 개발자에게 넘기기 전에 SVG 파일이 문제가 있는지 가볍게 확인하고 싶은 경우 아래와 같은 사이트를 추천해드립니다.http://inloop.github.io/svg2android/위에 문제가 되었던 수영장 아이콘을 이 사이트에 올려서 보게 되면 이런 화면이 뜹니다. Warning하고 노란색 경고 박스가 뜨게 되는데 fills-rule:evenodd에 대해서 언급을 하더라구요. 정말 유용한 사이트인 것 같습니다.아울러..많은 디자이너들이 SVG 적용을 해보시길 바라며 주변에 이 글도 많이 공유해주시면 감사하겠습니다. (ㅎㅎ)또한 데일리호텔 Tech, UI/UX 등의 정보를 얻어보고자 하시는 분은 https://dailyhotel.io/ 를 읽어 보시길 권장합니다.그럼 다음에도 좋은 정보로 찾아뵙겠습니다!원문 링크 : https://dailyhotel.io/디자인-안드로이드-앱-svg-아이콘-적용기-왜-svg로-갈아탔는가-99c57cd84240작성자 : Product팀 Rachel Kim#데일리 #데일리호텔 #개발자 #개발팀 #업무환경 #개발환경 #SVN
조회수 4313

파이썬 코딩 컨벤션

스포카 개발팀 문성원입니다. 저희는 (익히 아시다시피) 서버를 개발하는데 파이썬(Python)을 사용하고 있는데, 오늘은 이러한 파이썬 코드를 작성할 때 기준이 되는 코딩 컨벤션(Coding Convention)에 대해서 알아보겠습니다.Coding Convention코딩 컨벤션이란 개념에 대해 생소하신 분들도 계실 테니 이를 먼저 알아보죠. 코딩 컨벤션은 프로그램 코드를 작성할 때 사용되는 일종의 기준입니다. 이를테면 들여쓰기(Indentation)는 공백으로 할거냐 탭으로 할거냐. 부터 var a = 3; 과 같은 코드에서 a와 =를 붙이느냐 마느냐라던지를 정해주는 것이죠. 알고 계시는 것처럼 이러한 차이는 특별히 실행 결과의 영향을 주지 않습니다. 다르게 이야기하자면 “실행 결과에 별 차이가 없는 선택지들”이기 때문에 일관성이 있는 기준을 두어 통일하자는 것이지요.그렇다면 왜 이런 선택지를 통일해야 할까요? 불행히도 우리가 작성한 코드는 많은 사람들이 보게 됩니다. 같이 일하는 동료, 이바지하고 있는 프로젝트의 리뷰어, 심지어 내일의 자기 자신까지도 말이죠. 그런데 이런 많은 사람들이 우리가 코드를 작성할 때 했던 선택지를 일일이 추론해서 이해하는 건 굉장히 피곤하고 짜증 나는 일입니다. 그래서 우리는 사소한 것부터 일종의 규칙을 정해서 이런 짜증과 불편함을 줄이려는 겁니다. 또한, 일반적으로 좋은 기준에는 훌륭한 프로그래머들의 좋은 습관이 배어있기 때문에 더 나은 품질의 코드를 작성하는 데에도 많은 도움이 됩니다.이런 코딩 컨벤션은 극단적으로 이야기하면 프로젝트마다 하나씩 존재한다고 볼 수도 있지만, 일반적으로 그 언어문화를 공유하는 공동체에서 인정하는 컨벤션은 대부분 통일되어 있습니다. 파이썬은 지금부터 살펴볼 PEP 8이 대표적입니다.PEP?PEP(Python Enhance Proposal)이란 이름대로 본디 파이썬을 개선하기 위한 개선 제안서를 뜻합니다. 이러한 제안서는 새로운 기능이나 구현을 제안하는 Standard Track, (구현을 포함하지 않는) 파이썬의 디자인 이슈나 일반적인 지침, 혹은 커뮤니티에의 정보를 제안하는 Informational, 그리고 파이썬 개발 과정의 개선을 제안하는 Process의 3가지로 구분할 수 있습니다. (좀 더 자세한 사항은 PEP에 대해 다루고 있는 PEP인 PEP 1을 참고하세요.) 파이썬은 언어의 컨벤션을 이러한 제안서(Process)로 나타내고 있는데 이것이 바로 PEP 8입니다.Laplace’s Box기본적으로 가이드라인이니만큼 규칙만 빽빽할 것 같지만, PEP 8는 서두부터 예외를 언급한 섹션이 있습니다.A style guide is about consistency. Consistency with this style guide is important. Consistency within a project is more important. Consistency within one module or function is most important.스타일 가이드는 일관성(consistency)에 관한 것입니다. 이 스타일 가이드의 일관성은 중요하죠. 하지만 프로젝트의 일관성은 더욱 중요하며, 하나의 모듈이나 함수의 일관성은 더더욱 중요합니다.But most importantly: know when to be inconsistent – sometimes the style guide just doesn’t apply. When in doubt, use your best judgment. Look at other examples and decide what looks best. And don’t hesitate to ask!하지만 가장 중요한 건 언제 이것을 어길지 아는 것입니다. – 때때로 스타일 가이드는 적용되지 않습니다. 의심이 들 때는 여러분의 최선의 판단을 따르세요. 다른 예제를 보고 어느 게 제일 나은지 골라야 합니다. 질문을 주저하지 마세요!Two good reasons to break a particular rule:When applying the rule would make the code less readable, even for someone who is used to reading code that follows the rules.To be consistent with surrounding code that also breaks it (maybe for historic reasons) – although this is also an opportunity to clean up someone else’s mess (in true XP style).다음은 규칙들을 어기는 2가지 좋은 예외 사항입니다.규칙을 적용한 코드가 (규칙을 숙지한 사람 눈에도) 읽기 어려운 경우일관성을 지키려고 한 수정이 다른 규칙을 어기는 경우(아마도 역사적인 이유겠죠.)아직 아무것도 안나왔는데 좀 이르다구요?It’s all about common sense예외 규정을 보여주며 시작하는 PEP 8이지만 얼개는 그리 복잡하지도 않고 크게 난해하지도 않습니다. 여기서는 대표적인 몇 가지만 추려서 소개하겠습니다.Code lay-out들여쓰기는 공백 4칸을 권장합니다.한 줄은 최대 79자까지최상위(top-level) 함수와 클래스 정의는 2줄씩 띄어 씁니다.클래스 내의 메소드 정의는 1줄씩 띄어 씁니다.Whitespace in Expressions and Statements다음과 같은 곳의 불필요한 공백은 피합니다.대괄호([])와 소괄호(())안쉼표(,), 쌍점(:)과 쌍반점(;) 앞키워드 인자(keyword argument)와 인자의 기본값(default parameter value)의 = 는 붙여 씁니다.Comments코드와 모순되는 주석은 없느니만 못합니다. 항상 코드에 따라 갱신해야 합니다.불필요한 주석은 달지 마세요.한 줄 주석은 신중히 다세요.문서화 문자열(Docstring)에 대한 컨벤션은 PEP 257을 참고하세요.Naming Conventions변수명에서 _(밑줄)은 위치에 따라 다음과 같은 의미가 있습니다._single_leading_underscore: 내부적으로 사용되는 변수를 일컫습니다.single_trailing_underscore_: 파이썬 기본 키워드와 충돌을 피하려고 사용합니다.__double_leading_underscore: 클래스 속성으로 사용되면 그 이름을 변경합니다. (ex. FooBar에 정의된 __boo는 _FooBar__boo로 바뀝니다.)__double_leading_and_trailing_underscore__: 마술(magic)을 부리는 용도로 사용되거나 사용자가 조정할 수 있는 네임스페이스 안의 속성을 뜻합니다. 이런 이름을 새로 만들지 마시고 오직 문서대로만 사용하세요.소문자 L, 대문자 O, 대문자 I는 변수명으로 사용하지 마세요. 어떤 폰트에서는 가독성이 굉장히 안 좋습니다.모듈(Module) 명은 짧은 소문자로 구성되며 필요하다면 밑줄로 나눕니다.모듈은 파이썬 파일(.py)에 대응하기 때문에 파일 시스템의 영향을 받으니 주의하세요.C/C++ 확장 모듈은 밑줄로 시작합니다.클래스 명은 카멜케이스(CamelCase)로 작성합니다.내부적으로 쓰이면 밑줄을 앞에 붙입니다.예외(Exception)는 실제로 에러인 경우엔 “Error”를 뒤에 붙입니다.함수명은 소문자로 구성하되 필요하면 밑줄로 나눕니다.대소문자 혼용은 이미 흔하게 사용되는 부분에 대해서만 하위호환을 위해 허용합니다.인스턴스 메소드의 첫 번째 인자는 언제나 self입니다.클래스 메소드의 첫 번째 인자는 언제나 cls입니다.메소드명은 함수명과 같으나 비공개(non-public) 메소드, 혹은 변수면 밑줄을 앞에 붙입니다.서브 클래스(sub-class)의 이름충돌을 막기 위해서는 밑줄 2개를 앞에 붙입니다.상수(Constant)는 모듈 단위에서만 정의하며 모두 대문자에 필요하다면 밑줄로 나눕니다.Programming Recommendations코드는 될 수 있으면 어떤 구현(PyPy, Jython, IronPython등)에서도 불이익이 없게끔 작성되어야 합니다.None을 비교할때는 is나 is not만 사용합니다.클래스 기반의 예외를 사용하세요.모듈이나 패키지에 자기 도메인에 특화된(domain-specific)한 기반 예외 클래스(base exception class)를 빌트인(built-in)된 예외를 서브클래싱해 정의하는게 좋습니다. 이 때 클래스는 항상 문서화 문자열을 포함해야 합니다.class MessageError(Exception): """Base class for errors in the email package."""raise ValueError('message')가 (예전에 쓰이던) raise ValueError, 'message'보다 낫습니다.예외를 except:로 잡기보단 명확히 예외를 명시합니다.(ex. except ImportError:try: 블록의 코드는 필요한 것만 최소한으로 작성합니다.string 모듈보다는 string 메소드를 사용합니다. 메소드는 모듈보다 더 빠르고, 유니코드 문자열에 대해 같은 API를 공유합니다.접두사나 접미사를 검사할 때는 startswith()와 endwith()를 사용합니다.객체의 타입을 비교할 때는 isinstance()를 사용합니다.빈 시퀀스(문자열, 리스트(list), 튜플(tuple))는 조건문에서 거짓(false)입니다.불린형(boolean)의 값을 조건문에서 ==를 통해 비교하지 마세요.Give me a reason하지만 몇몇 규칙은 그 자체만으론 명확한 이유를 찾기 어려운 것도 있습니다. 가령 예를 들면 이런 규칙이 있습니다.More than one space around an assignment (or other) operator to align it with another.Yes:x = 1 y = 2 long_variable = 3No:x = 1 y = 2 long_variable = 3보통 저런 식으로 공백을 통해 =를 맞추는 건 보기에도 좋아 보입니다. 하지만 변수가 추가되는 경우에는 어떨까요. 변수가 추가 될때마다 공백을 유지하기 위해 불필요한 변경이 생깁니다. 이는 소스를 병합(merge)할 때 혼란을 일으키기 쉽습니다.언뜻 보면 잘 이해가 안 가는 규칙은 이런 것도 있습니다.Imports should usually be on separate lines, e.g.:Yes: import os import sys No: import sys, os굳이 한 줄씩 내려쓰면 길어지기만 하고 보기 안 좋지 않을까요? 하지만 이 역시 대부분의 변경 추적 도구가 행 기반임을 고려하면 그렇지 않습니다.#스포카 #개발 #파이썬 #개발자 #Python #컨벤션 #이벤트참여 #이벤트후기 #후기
조회수 880

프로그래밍에는 왜 창의성이 필요하다고 할까요?

왜 프로그래밍에는 창의성이 필요하다고 할까요? 실제로 프로그래밍을 하다 보면 복잡한 문법을 이해하고, 암호 같은 에러를 차분히 해결해야 하니, 오히려 수학적이며 논리적인 사고가 더 필요해 보입니다. 그런데도 프로그래밍이 창의적이어야 하는 이유는 하나의 프로그램을 만드는 답이 여럿이기 때문입니다.답이 하나여도 가르치기 어려운데, 다양한 방법을 어떻게 가르쳐야 하는 걸까요? 또, 기상천외한 학생들의 코드를 보며 이해하고 교육하는 것이 얼마나 긴 시간이 필요하며 어려운 일인가요? 어디서부터 해결해나가야 할지 막막합니다.Elice 리서치 팀에서 하는 일 중 하나는 학생들의 수많은 코드 중 비슷한 타입들을 추려내는 것입니다. 코드를 몇 가지 타입으로 추려내고 나면, 선생님은 학생 하나하나의 코드를 보고 교육하는 것이 아니라, 비슷한 형태의 코드를 작성한 학생 그룹 전체에게 적절한 피드백을 줄 수 있습니다. 이렇게 그룹 전체에게 피드백을 준다면 선생님은 같은 시간에 더 많은 학생을 교육할 수 있을 것입니다.그럼 이제, 비슷한 코드를 어떻게 찾아내서 분류할지 이전 연구를 보며 알아봅시다. (현기증이 날 수 있으며, 다 이해할 수 없어도 괜찮으니 걱정하지 마세요!)지프의 법칙과 숙제 제출 패턴자연어 처리(Natural Language Processing;NLP)를 배울 때 자주 거론되는 사람이 있습니다. 바로 미국의 언어학자인 조지 킹슬리 지프George Kingsley Zipf인데, 이 사람이 만든 지프의 법칙은 자연적으로 일어나거나 생성되는 특정 정보들이 일정하게 나타내는 경향을 나타낸 것입니다. 지프는 영어로 된 텍스트를 분석하던 도중, 자주 쓰이는 단어를 순서대로 나열하면 각 단어의 빈도는 그 단어의 출현 순위에 반비례함을 찾아냈습니다. 영어에서 가장 많이 사용되는 단어 1~3위가 “the”, “of”, “and” 인데, “the”는 “of”의 약 두 배, “and”의 약 세 배의 빈도를 보입니다.이것을 수학적으로 표현하면, 일정 크기 이상의 영어 말뭉치(corpus)에 들어 있는 단어들의 개수를 전부 세서 그 단어들을 가장 많이 쓰이는 것부터 순위를 1위부터 나열했을 때, 특정 단어의 순위가 k 라면 (즉 전체에서 k번째로 많이 쓰인 단어라면) 그 단어가 말뭉치에서 쓰인 개수는 1/k에 비례한다는 것입니다. 이것을 그래프로 그려보면 다음과 같습니다. 재미있는 사실은, 여기에서 x축과 y축에 log를 씌워 보면 (이것을 log-log scale로 변환한다고 합니다.) 다음과 같은 직선 형태로 변환된다는 것입니다.이것이 도대체 숙제 제출과 무슨 관계가 있길래 이렇게 장황한 설명을 한 것일까요? 위에서 나온 지프의 법칙을 기억하시나요? 학생이 낸 숙제를 채점하다 보면 꽤 많은 학생이 비슷한 방식으로 숙제를 푸는데, 제출된 풀이 방식들을 비슷한 것끼리 묶어 분석해 보니, 이것 또한 지프의 법칙을 따른다는 것이 발견되었습니다. 예를 들어 가장 인기 있었던 풀이 방식으로 100명이 숙제를 제출했다면, 두 번째로 인기 있는 풀이 방식으로는 약 50명이 숙제를 제출했다는 뜻입니다.여기서 우리가 찾아낼 수 있는 인사이트는 무엇일까요? 첫째, 학생들의 숙제들을 비슷한 것끼리 묶을 수 있다면, 그리고 이 분류를 컴퓨터로 자동으로 할 수 있다면 조교가 채점하거나 코멘트를 할 때 써야 할 시간이 상당히 줄어들 것입니다. 둘째, 방법 서너 개에 대해서만 어떻게 채점할지 혹은 어떻게 코멘트할지에 대해 준비를 해놓으면, 그걸로 숙제 대부분을 채점/코멘트할 수 있을 것입니다. 대다수의 숙제는 몇 가지 인기 있는 풀이방식으로 만들어졌을 것이기 때문입니다.그러면 이제 다음 문제는, ‘비슷한 풀이 방식으로 푼 프로그램 코드’를 어떻게 찾아낼 것인가? 를 고민해봅니다. MIT의 Elena Glassman은 이에 대한 해법으로 Overcode를 제시했습니다.Overcode뉴스 기사나 책, 블로그 글 등 자연어로 이루어진 텍스트 데이터를 분석하고, 여기에 어떤 주제가 들어있는지 밝혀내는 연구는 많이 진행 됐습니다. 이를 위한 머신러닝 알고리즘 중 하나가 토픽 모델, Topic model 입니다(토픽 모델에 대해서는 다른 글에서 자세히 다룰 예정입니다). 그러나 토픽 모델링을 프로그래밍 문제에 실제로 적용하기는 쉽지 않습니다. 코드에 사용되는 문법이나 키워드가 자연어와 1:1로 매칭되지 않기 때문에, 기존에 자연어에서 사용되던 모델을 그대로 사용할 수 없기 때문입니다. 가령, 슬쩍 보면 무척 달라 보이는 아래 두 파이썬 코드는 사실 완전히 같게 동작합니다. 이 두 코드를 (사람들이 일상생활에서 사용하는) 자연어를 분석하는 모델로 분석한다면 제대로 된 결과를 낼 수 없는 건 당연합니다.def fibonacci(): parents, babies = (1, 1) while babies < 100 xss=removed>fibonacci()def fib(parents, babies): ‘’’ parents = 1 babies = 1 ‘’’ while True: print ‘This generation has {0} babies’.format(babies) parents = babies # set parents as babies babies = parents + babies # recursively add number of babies if babies >= 100: break fib(1, 1)Elena Glassman이 제시한 Overcode의 목적은 비슷한 로직으로 만들어진 프로그래밍 코드들을 모으는 것입니다. 이제 Overode가 어떻게 작동하는지 간단하게 소개하도록 하겠습니다. 가장 먼저 수행되어야 하는 것은 서로 다른 형식으로 쓰인 소스 코드들을 정리하는 것입니다. 소스코드 정리에는 주석 제거, 줄 및 공백/들여쓰기를 일정하게 맞추는 작업 등이 포함됩니다.Image from Overcode하지만 이 작업만으로는 충분하지 않습니다. 거의 같아 보이는 코드도 실제로 프로그램을 실행하기 전까지는 같은지 알 수 없고, 꽤 달라 보이는 코드도 실제로는 완전히 같게 동작할 수 있기 때문입니다 (여기서 같게 동작한다 함은, 결과를 같게 내는 것 이상으로 결과를 내는 중간과정이 완전히 같다는 것을 의미합니다). 다시 위로 돌아가, fibonacci() 함수와 fib(parents, babies) 함수를 살펴봅시다. 위 두 코드는 기존의 자연어 처리 기법에 따르면 완전히 다른 프로그램일 것입니다. 변수명이 달라서가 가장 큰 이유일 텐데, 사실 컴퓨터의 입장에서 변수는 어떤 값을 할당하는 공간에 불과하며, 그 공간에 어떤 이름을 붙이느냐는 중요하지 않습니다. 코드를 작성하는 것이 사람이기 때문에 공간에 편하게 이름을 붙이는 것뿐입니다. 서로 다른 프로그램에서 어떤 변수가 서로 같은 역할을 하는지, 컴퓨터가 알아내려면 어떻게 해야 할까요? (컴퓨터는 창의적이지 않습니다!)Image from OvercodeElena가 제시한 방법은 프로그램을 실행하면서 변수의 값이 어떻게 바뀌는지를 추적한 것입니다. 아래 두 코드를 보고, 한번 머릿속으로 프로그램을 실행해 봅시다. 학생 B의 코드는 for문으로 5의 3승을 계산했고, 학생 C의 코드는 while문으로 5의 3승을 계산한 것입니다. 학생 B의 코드가 실행됨에 따라 r이라는 변수가 어떻게 변하는지, 학생 C의 코드에서 result가 어떻게 변하는지 확인해보면 둘 다 1 → 5 → 25 → 125 의 값을 가지게 됩니다. 그렇다면 컴퓨터는 이렇게 판단할 수 있습니다. “B의 코드에서의 변수 r과 C의 코드에서의 result가 완전히 같은 방식으로 변하니, 같은 의미로 사용된 것이다.”Image from Overcode이제 같은 의미를 가지는 변수들을 알아냈다면, 컴퓨터는 쉽게 가장 “흔한” 이름으로 변수의 이름을 바꿔치기 합니다. 그러면 처음에 서로 다르게 보였던 코드들도 이제 같아질 것입니다.물론 이것이 다는 아닙니다. 예를 들어, 간단한 테스트 케이스들을 통해 결과를 비교함으로써 변수를 분석하기 전에 먼저 거르는 방법, 너무 흔한 변수들을 처리하는 방법 (예를 들어 완전히 다르게 동작하는 코드들에서도 반복문에서 사용되는 인덱싱 변수들은 같이 변화할 것입니다), 한 변수가 다른 의미론적으로 두 번 사용되는 경우 처리하는 방법… 등이 논문에는 더욱 자세히 적혀있습니다. 궁금한 독자들은 한번 논문을 읽어보도록 합시다.남은 문제들Elena가 제시한 방법은 위에서 보여준 예제와 같은 간단한 코드에서 꽤 잘 동작합니다. 예를 들어, 다음과 같은 문제들이 있습니다.a의 b승을 구해서 리턴하는 프로그램N번째 피보나치 숫자를 리턴하는 프로그램다항식의 미분 결과를 리턴하는 프로그램하지만 대학교에서 1학년만 넘어가더라도 이런 간단한 프로그램 과제는 내지 않습니다. 예를 들어 Elice에서 교육 중인 기초 프로그래밍/ 프로그래밍 유치원 수업을 듣는 학생들은 매우 많은 실습문제를 풉니다. 여기에서 푸는 과제들은 초반 몇 주가 지나면 이 정도의 간단한 프로그래밍 수준을 뛰어넘기 때문에, 코드가 꽤 길어지고 다양성이 생기게 되는데 이런 경우 이 방법은 잘 동작하지 않습니다.또 다른 문제는, 이 방법이 “동작하는” 코드에서만 작동한다는 것입니다. 예를 들어, 수강생들이 아직 기초 문법을 배우고 있다면? 제대로 실행도 되지 않는 코드를 만들었을 때, 비슷한 실수를 한 사람들끼리 묶어주고 싶다면? 아쉽게도 Elena가 제시한 방법은 이렇게 에러가 나는 코드에서는 동작하지 않습니다. 코드가 실행되지 않는다면 변수의 값의 변화를 추적할 수 없기 때문입니다.마치며이번 포스트에서는 학생의 제출 코드를 비슷한 것끼리 묶는(Clustering하는) 방법에 대해 간단하게 살펴 보았습니다. 학생이 낸 비슷한 답안을 모아주는 솔루션은 수학 문제 같은 단답식 문제, 혹은 영어 에세이같은 자연어에 대해서는 이미 상용화가 되어 있습니다. 영어 에세이의 경우 여러분들이 가장 친숙할 만한 상용화된 솔루션은 아마 copy detector일 것입니다.하지만 프로그래밍 코드의 클러스터링은 연구가 계속 진행되고 있습니다. 앞서 말했듯 코드에서 한 글자 한 글자가 가지는 의미가 자연어에서 가지는 알파벳과는 완전히 다르기 때문이기도 하고, 정말 실행을 해 보기 전까지는 어떻게 동작하는지 예측하는 것이 매우 어렵기 때문이기도 합니다. Elice 리서치 팀에서도 프로그래밍 코드에 대한 분석을 자동으로 수행하는 머신러닝 연구를 수행하고 있습니다. 이러한 기술을 통해 선생님이나 조교가 학생을 더욱 효율적으로 지도하고, 컴퓨터의 도움으로 지도에 아낀 시간을 한 단계 더 개인화된 도움을 주도록 하는 것이 Elice의 목표 중 하나 입니다.글쓴이김수인: KAIST 전산학부 박사과정 / Research Lead, Elice김재원: KAIST 전산학부 박사과정 / The Lead, Elice배휘동: KAIST 전산학부 박사과정 / Frontend Lead, Elice#엘리스 #코딩교육 #교육기업 #기업문화 #조직문화 #서비스소개
조회수 856

웹기반 컨텐츠 저작 도구 셀프(XELF) v1.0 GS인증 획득

웹기반 컨텐츠 저작 도구 셀프(XELF) v1.0 (Web-based Contents Authoring Tool XELF v1.0)이 한국정보통신기술협회(TTA) 소프트웨어 시험인증연구소로부터 GS인증 1등급을 획득하였습니다.  셀프(XELF)는 별도의 프로그램 설치 없이도 접속만으로 웹브라우저 상에서 다양한 용도의 콘텐츠를 저작할 수 있는 디자인 플랫폼입니다. 디자인 전문가가 아니어도 누구나 손쉽게 프리젠테이션, 웹브로셔, 유저 인터페이스, 문서 등 비즈니스 및 교육환경에 필요한 다양한 콘텐츠를 디자인할 수 있습니다. 또, 이렇게 제작된 콘텐츠는 클릭만으로 SNS에 공유하거나 이메일로 전달하는 등 간편하게 활용할 수 있는 장점을 가지고 있습니다.   GS인증은 엄격한 시험을 통해 품질이 우수한 소프트웨어를 인증해주는 국가공인 소프트웨어 품질인증제도로 공공기관에서 우선 구매 대상으로 지정되기도 합니다. ISO 국제표준을 기준으로 SW의 기능성, 신뢰성, 효율성, 사용성, 유지보수성, 이식성, 성능 등을 평가하고 검증을 거쳐 부여되었습니다. ㈜그로비스인포텍은 이번 GS인증을 계기로 디자인 플랫폼으로서의 기술성과에 자신감을 가지고 향후 계획된 베타서비스 준비에 최선을 다하고 있습니다. 더 나은 사용성과 기술적 안정성을 목표로 다양한 환경에 적용하고 테스트를 진행하고 있습니다. 곧이어 더 향상된 성능과 기능으로 찾아뵙길 기대하겠습니다. 감사합니다.

기업문화 엿볼 때, 더팀스

로그인

/