<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>noly-poly</title>
        <link>https://velog.io/</link>
        <description>Junior Back-End Developer</description>
        <lastBuildDate>Mon, 19 Dec 2022 13:01:42 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>noly-poly</title>
            <url>https://velog.velcdn.com/images/noly-poly/profile/374bd996-eb1e-41d1-8700-d1dd90a39e6a/image.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. noly-poly. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/noly-poly" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[Darling, 커플 플랫폼, 2021]]></title>
            <link>https://velog.io/@noly-poly/darling</link>
            <guid>https://velog.io/@noly-poly/darling</guid>
            <pubDate>Mon, 19 Dec 2022 13:01:42 GMT</pubDate>
            <description><![CDATA[<h1 id="개요">개요</h1>
<h2 id="서비스">서비스</h2>
<h3 id="미리보기">미리보기</h3>
<p><img src="https://velog.velcdn.com/images/noly-poly/post/5df0d394-8c47-4437-a2a7-a952af342bd2/image.png" alt=""></p>
<hr>
<h3 id="소개">소개</h3>
<ul>
<li>서비스 명: Darling, 달링</li>
<li>서비스 주제: 커플 플랫폼</li>
<li>서비스 플랫폼: iOS App</li>
</ul>
<hr>
<h3 id="핵심기능">핵심기능</h3>
<ul>
<li>두 명의 사용자가 커플로 연동할 수있다.</li>
<li>상대에게 &#39;꾹 찌르기&#39; 푸시 알림을 보낼 수 있다.</li>
<li>다가오는 디데이를 알 수 있다.</li>
<li>커플 쿠폰을 주고 받을 수 있다.</li>
<li>위시 리스트를 작성하고 클리어 할 수 있다.</li>
<li>장소를 기반으로 한줄일기를 작성할 수있다.</li>
</ul>
</br>

<h2 id="프로젝트">프로젝트</h2>
<h3 id="규모">규모</h3>
<ul>
<li><strong>기간</strong>: 3달
21.07.02 ~ 21.09.17</li>
<li><strong>인원</strong>: 3명<ul>
<li>✋ <code>PM</code>
네이티브 iOS App 개발</li>
<li>👩‍💻
<del>네이티브 Android App 개발</del>, 프로젝트 리팩토링, 배포</li>
<li>👨‍💻
API 서버 구현</li>
</ul>
</li>
</ul>
<hr>
<h3 id="기술스택">기술스택</h3>
<ul>
<li><strong>언어</strong><ul>
<li>Swift/SwiftUI2.0</li>
<li>Java</li>
<li>MySql</li>
</ul>
</li>
<li><strong>프레임워크</strong><ul>
<li>Spring Boot</li>
</ul>
</li>
<li><strong>API</strong><ul>
<li>Social Login(Kakao, Google, Apple)</li>
<li>Firebase FCM Notification</li>
<li>Apple In-App Purchase API</li>
</ul>
</li>
<li><strong>라이브러리</strong><ul>
<li>Alamofire</li>
<li>SwiftyJSON</li>
<li>AlertToast</li>
<li>SDWebImageSwiftUI</li>
<li>JPA</li>
<li>QueryDsl</li>
<li>Lombok</li>
<li>okhttp3</li>
<li>Jasypt</li>
</ul>
</li>
</ul>
</br>
</br>
</br>

<h1 id="기획">기획</h1>
<h2 id="1-커플-서비스의-경험">1. 커플 서비스의 경험</h2>
<h3 id="1-1-장소기반-다이어리">1-1. 장소기반 다이어리</h3>
<p>2017년 &#39;Loviary&#39;라는 커플이 장소를 기반해 다이어리를 간단하게 적을 수 있는 서비스를 php기반 CMS로 제작했었던 적이 있었다.
<img src="https://velog.velcdn.com/images/noly-poly/post/2e2d0c1f-bb21-41c5-8dda-59f75c46c6b7/image.png" alt=""></p>
<p>장소별 한마디, 오늘의 한줄평, 기분을 담고있는 다이어리를 작성해서 사귀고 난 후 몇 일의 데이트인지, 몇 번째 데이트인지 안내하고 장소별로 검색을 하는 기능과 다가오는 기념일들의 디데이를 제공했다. 오늘의 한줄평을 누가 썼는지, 둘 다 썼는지도 직관적으로 확인이 가능했다. 테스터들의 반응이 괜찮았었던 서비스였다.</p>
<h3 id="1-2-커플-쿠폰">1-2. 커플 쿠폰</h3>
<p>올해 초에 필요에 의해서 커플 쿠폰 서비스 &#39;알로하 쿠폰&#39;을 출시했다. 이 서비스에 디데이와 위시 리스트, 그리고 &#39;Loviary&#39;의 장소기반 한줄 다이어리를 하나의 서비스에서 사용할 수 있으면 좋겠다고 생각이 들어다. 그래서 바로 팀을 꾸렸고 한창 JPA를 사용하는 API 서버 개발에 흥미가 넘치고 있었기에 서버를 맡고 싶었지만, iOS 앱 제작 경험이 있는 사람이 없는 관계로 내가 맡게 되었다. 추가로 Android를 맡으며 서버를 멘토링해줄 팀원, 서버 개발 경험이 부족한 팀원과 함께 총 3명이서 프로젝트 시작하게 되었다. </p>
<h2 id="2-커플-서비스의-총집합">2. 커플 서비스의 총집합</h2>
<h3 id="2-1-하나의-커플-서비스">2-1. 하나의 커플 서비스</h3>
<p>일단 기획적인 부분은 기존의 서비스들을 일체화 시키는 것이기 때문에 서비스의 흐름과 기능들의 디테일만 신경쓰면 됐다. 모바일 앱으로 제작이 될 예정이기 때문에 홈화면은 대시보드 역할의 뷰, 나머지 주제들은 각각의 뷰로 하단탭을 구성하기로 했다. 하지만 프로젝트 진행 중에 알림 내역까지 하단바에 추가하게 되었는데, 주제별 뷰가 너무 많다고 판단하여 하나의 하단탭이지만 상단탭을 추가로 구성하였다.</p>
<p><img src="https://velog.velcdn.com/images/noly-poly/post/8187ed9c-1a95-4c99-8b9a-46e295a408cd/image.png" alt=""></p>
</br>
</br>
</br>

<h1 id="uiux">UI/UX</h1>
<h3 id="메인화면">메인화면</h3>
<p>각자의 프로필 사진, 한마디와 함께 찍은 사진 콕 찌르기와 같은 기능이 포함되어 있어야 했는데 오브젝트 배치에 대한 고민을 하다가 iOS의 홈화면을 참고하기로 했다. 정보들과 사진의 위젯이 있어도 친숙하고 못 나지 않은 디자인이 가능했다. 설정에 메뉴로 존재하지만 각각의 위젯들을 터치하면 수정할 수 있는 페이지로 바로 연결되도록 viewflag로 컨트롤 했다. 
<img src="https://velog.velcdn.com/images/noly-poly/post/5587cb44-49ff-4303-85cd-3be11d9ee2ec/image.png" alt=""></p>
<h3 id="서비스-뷰">서비스 뷰</h3>
<p>따로 하단바에 서비스들을 묶어놓은 탭을 만든것 까지는 좋았지만 테스터들의 불편사항이 있었다. 터치가 늘어났는데 손가락 동선이 최하단에서 최상단까지 가야하는 번거로움이 있었기 때문인데, 이 부분은 서비스 뷰의 하단탭 아이콘이 활성화된 상태에서 중복으로 터치하게 되면 상단바 탭이 현재의 오른쪽 탭으로 전환되는 기능을 추가해 해소시켜주었다. 그래서 하단에만 손가락이 위치하더라도 모든 서비스를 접근할 수 있게 되었다.
<img src="https://velog.velcdn.com/images/noly-poly/post/fa98c656-2fdd-4856-ae61-7956b8676834/image.png" alt=""></p>
</br>
</br>
</br>
# 개발

<h2 id="1-라이브러리">1. 라이브러리</h2>
<h3 id="1-1-콕-찌르기-alerttoast">1-1. 콕 찌르기, AlertToast</h3>
<p>콕찌르기 기능은 페이스북에 먼저 있던 기능인데 커플끼리도 사용하기 좋을 것 같아 추가됐다. 기능은 아주 단순하다. 내가 콕 찌르기를 누르면 상대에게 상대가 콕 찔렀다고 알림이 전송된다. 다양한 상황에서 쓸 수 있었던 것 같다. 그리고 테스터들에게도 가장 좋은 평을 받은 기능이 의외로 이 기능이었다. 그래서 인터렉션까지는 아니더라도 애니메이션 효과를 추가해주고 싶었다. 그래서 찾은 라이브러리가 <a href="https://github.com/elai950/AlertToast">AlertToast</a>이다. 디자인을 바꿔 적용하여 상대에게 콕 찔렀다는 경험을 높일 수 있었다.
<img src="https://velog.velcdn.com/images/noly-poly/post/6ba76d9c-fb1c-4e27-afdf-22fd3c7f2f79/image.png" alt=""></p>
<h3 id="1-2-이미지-sdwebimage">1-2. 이미지, SDWebImage</h3>
<p>이전 iOS 앱을 개발할때는 단순히 앱에 이미지를 포함하고 불러오는 형식만 사용했었다. 이번에는 <a href="https://github.com/SDWebImage/SDWebImageSwiftUI">SDWebImageSwiftUI</a>를 사용하게 되었는데 장점이 아주 많다. 캐시 처리가 되지 않고 매번 새로 불러오는 과정을 겪는게 골치 덩어리였는데 해결이 되었고, 외부 경로(URL)로 가져오는 이미지를 간편하게 사용할 수 있다. gif같은 애니메이션을 지원하는 이미지도 사용할 수 있다.</p>
<h2 id="2-스위프트ui">2. 스위프트ui</h2>
<h3 id="2-1-하단바">2-1. 하단바</h3>
<pre><code class="language-swift">// TabView 예시
 @State var viewFlag = 0

 TabView(selection:viewFlag) {
            FirstView(viewFlag:$viewFlag)
                .tabItem({
                    Image(systemName: &quot;arrow.left&quot;)
                    Text(&quot;First&quot;)
                }).tag(0)
            SecondView(viewFlag:$viewFlag)
                .tabItem {
                    Image(systemName: &quot;arrow.right&quot;)
                    Text(&quot;Second&quot;)
                }.tag(1)
}</code></pre>
<pre><code class="language-swift">// View Struct 예시
struct FirstView: View {
    @Binding var viewFlag:Int
    var body: some View {
        Button(&quot;Second View&quot;) {
            self.viewFlag = 1
        }
    }
}

struct SecondView: View {
    @Binding var viewFlag:Int
    var body: some View {
        Button(&quot;First View&quot;) {
            self.viewFlag = 0
        }
    }
}
</code></pre>
<p>TabView()를 이용헤서 하단바를 구성했다. tabItem의 tag로 View마다 index를 지정해주고, State 변수로 제어하며 struct 내부에 State 변수를 Binding함으로서 내부에서도 버튼을 이용한 화면 흐름을 관리할 수 있게 했다.</p>
<h3 id="2-2-상단바">2-2. 상단바</h3>
<pre><code class="language-swift">@State var tabFlag: String = &quot;위시리스트&quot;
let filterOption: [String] = [
    &quot;디데이&quot;, &quot;한줄일기&quot;, &quot;쿠폰&quot;, &quot;위시리스트&quot;
]</code></pre>
<pre><code class="language-swift">Picker(
    selection: $tabFlag,
    content:{
        ForEach(filterOptions.indices) { index in
            Text(filterOptions[index])
            .tag(filterOptions[index])
        }
    })
.pickerStyle(SegmentedPickerStyle())
</code></pre>
<p>개인적으로 상단바에 화면 정보를 표시하는 것 이외의 용도로 사용하는 것을 사용자 경험적인 측면에서 선호하지 않아 간단하게 Navigation Bar로 처리하고, 개별 View 안에서 상단 탭에서 목적에 맞는 탭을 제공하게 했다. 상단 탭은 Picker의 style 중 SegmentedPickerStyle()을 사용했다. 모든 탭의 작성하는 화면은 UISheetPresentationController를 사용해 Modal의 형태로 제공했다.</p>
<p><img src="https://velog.velcdn.com/images/noly-poly/post/73b55fc3-4a06-4e25-90dc-215e4ea4e3a6/image.png" alt=""></p>
<h2 id="3-ios-위젯">3. iOS 위젯</h2>
<h3 id="widgetkit">WidgetKit</h3>
<p>iOS의 위젯을 제작해보는 것은 처음이었다. iOS 14부터 홈 화면에 추가 할 수 있는 앱 위젯이 새로 생겼는데 마침 안 그래도 Swift 최신 버전의 레퍼런스도 많이 없는데 WidgetKit에 대한 레퍼런스를 찾기가 어려웠다. 그래서 하는 수 없이 몇 없는 자료들로 하나하나 테스트해보며 학습하면서 적용해나갔다. 화면을 구성하는 방식은 앱과 유사했지만 동작 방식은 완전히 달랐다.</p>
<h3 id="타임라인-동기화">타임라인 동기화</h3>
<p>타임라인을 관리하는 것이 유난히 어려웠다. 서버에서 데이터를 받아와 반영해야하기 때문인 것으로 추측되는데, TimelineReloadPolicy(새로운 타임라인 요청 시기)가 설정 값대로 반영되지 않아 골머리를 썩혔다. 그래서 어쩔 수 없이 가능핱 즉시 타임라인을 요청하는 never 옵션으로 설정했다.</p>
<h3 id="위젯-갤러리">위젯 갤러리</h3>
<p><img src="https://velog.velcdn.com/images/noly-poly/post/268527ee-93d8-4259-a61d-8f9c1a353e04/image.png" alt=""></p>
<p>위젯을 테스트를 해보는데 텍스트로만 구성했던 테스트 위젯도 사라지고 새로 짜여진 위젯이 위젯 갤러리에 반영이 되야 했지만 앱에 대한 위젯 정보가 사라지고 당최 추가되지 않았다. 그래서 앱을 삭제하고 다시 설치해보았고 테스트 기기를 재부팅해보았지만 그래도 아무 소용이 없었다. 머리를 환기시키고 돌아왔더니 아무 것도 건드리지 않았는데 추가가 되어있었다. 반영이 즉각적으로 되지 않고 간헐적으로 아주 오래 걸릴때도 있다는 것을 알게 되었다. 이후 테스트에서는 잘 반영이 되었는데, TestFlight로 QA를 할때 또 추가되지 않았고 코드를 다시 검토해보았는데 문제가 없어 재부팅을 권하고 해결이 되어 마무리가 되었다.</p>
<h2 id="4-git">4. Git</h2>
<h3 id="aws-access-key">AWS Access Key</h3>
<p><img src="https://velog.velcdn.com/images/noly-poly/post/0383945c-4a04-4866-8690-c3d27906d512/image.png" alt="">
포트폴리오 목적으로 Github의 레포지토리를 공개 옵션으로 설정해두었었다. 그런데 몇일간 긴급 메일이 나에게 왔었고 이유는 AWS의 Access Key를 암호화해두지 않아서 노출되었고 승인되지 않고 비정상적인 패턴이 감지되어 바로 절차를 따르라는 안내였다. 깜짝 놀라서 바로 대응을 했고 운이 좋게도 큰 문제는 없었지만, 하마터면 해커들에게 악용당해 엄청난 비용이 청구될수도 있었다. 값진 경험을 했다.</p>
<h3 id="xcode와-swift">Xcode와 Swift</h3>
<p>새로운 플랫폼, 언어, 툴로 하다보니 당연했던 Git으로 버전관리를 하는 것을 너무나 자연스럽게 잊고 있었다. 이전의 aloha coupon 프로젝트 때도 그랬고 이번 프로젝트에서도 그랬다. 전에는 별일이 없었는데 이번에는 프로젝트를 날려버리는 사건이 있었다. 프로젝트 옵션에 대한 부분을 커스텀하다가 프로젝트 셋팅에 관련된 매핑 정보를 건드린 것 같은데 프로젝트를 전체를 인식할수가 없었다. 코드는 분명 살아있었는데 여러 시도를 해보아도 돌릴 수 가 없었다. 멘탈이 완전히 나가버렸지만 결국에 밤을 세워서 복구를 했다. 새로운 프로젝트를 만들어 경로를 잡아주고 텍스트 에디터로 코드를 일일이 확인해서 복사한 후 이미지와 자료 그리고 다른 라이브러리들도 추가 해주는 <del>노가다</del>작업을 했다.</p>
<p>그럼에도 불구하고 왜 그때서라도 버전 관리를 생각하지 않았는가. 너무 바보같다. 데드라인에 너무 촉박하게 진행이 됐거나, 개발이 진행되는 기능들의 순서가 너무 불규칙해서라는 이유였던 것 같은데 그 이유이였다면 브랜치를 활용하는 방법을 잘 몰랐음을 깨닫는다.</p>
<p>이유를 불문하고 버전관리는 필수이니 명심해야겠다.</p>
<h1 id="마치며">마치며</h1>
<h3 id="web과-app">WEB과 APP</h3>
<p>이번이 Swift는 프로젝트로는 두번째긴 하지만 여전히 느끼는 것은 첫 인상처럼 완전히 다르지 않다는 것이다. 많은 언어들이 객체지향 프로그래밍을 향해 가고 있기 때문에 그렇게 느낀 것 같다. &quot;코어 언어 하나를 제대로 이해한다면, 다른 언어를 배우는 것은 쉬운 일이 될 것이다.&quot;라고 말씀을 들은 기억이 떠오른다. 그리고 추가로 언어마다 특색 및 장단점, 프레임워크를 배우는 것은 중요하다는 것을 새삼 느끼게 된다. 새로운 타지로 여행을 다녀오면서 접하는 문화와 만나는 사람들을 통해 얻는 인사이트와 같이 개발도 다르지 않다고 생각한다.</p>
<h3 id="협업">협업</h3>
<p>API 서버만 다룰때는 고려하지 못한 것들이 프론트 웹이나 앱을 개발하는 포지션을 맡으면서 데이터 구조를 어떻게 전달하면 좋을지 알게되는 것 같다. 그리고 전체적인 포지션에 대한 경험이 늘수록 반대 상황일때도 소통이 수월하다는 것도 느꼈다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[SPACE, 프로젝트 관리 툴, 2021]]></title>
            <link>https://velog.io/@noly-poly/space</link>
            <guid>https://velog.io/@noly-poly/space</guid>
            <pubDate>Tue, 13 Dec 2022 03:19:54 GMT</pubDate>
            <description><![CDATA[<h1 id="개요">개요</h1>
<h2 id="서비스">서비스</h2>
<h3 id="미리보기">미리보기</h3>
<p><img src="https://velog.velcdn.com/images/noly-poly/post/927cef36-7e30-4117-af5d-5a2181189963/image.png" alt=""></p>
<hr>
<h3 id="소개">소개</h3>
<ul>
<li>서비스 명: SPACE, 스페이스</li>
<li>서비스 주제: 프로젝트 관리 툴</li>
<li>서비스 플랫폼: DESKTOP WEB</li>
</ul>
<hr>
<h3 id="핵심기능">핵심기능</h3>
<ul>
<li>프로젝트를 생성해 팀원을 추가할 수 있다.</li>
<li>TDD 칸반보드 형태로 업무를 관리할 수 있다.</li>
<li>업무는 회의의 안건과 실행 계획으로 참조할 수 있다.</li>
<li>업무와 회의에서 참조된 항목을 조회할 수 있고 바로 이동이 가능하다.
(자연스러운 업무-회의 순환구조)</li>
<li>프로젝트 팀원과 메시지를 주고 받을 수 있다.</li>
</ul>
<hr>
<h3 id="fbs">FBS</h3>
<p><img src="https://velog.velcdn.com/images/noly-poly/post/92ec20ac-dd92-4adf-a3df-0777d5b4510c/image.png" alt=""></p>
<hr>
<h3 id="erd">ERD</h3>
<p><img src="https://velog.velcdn.com/images/noly-poly/post/e3ded32f-6349-4a03-a1b7-33bcba14563e/image.png" alt=""></p>
</br>

<h2 id="프로젝트">프로젝트</h2>
<h3 id="규모">규모</h3>
<ul>
<li>기간: 한달
21.05.20 ~ 21.06.21</li>
<li>인원: 3명<ul>
<li>✋ PM
도메인 설계, API 서버 기능 구현(회원, 프로젝트, 팀원, 메세지)</li>
<li>👨‍💻
API 서버 기능 구현(업무, 회의)</li>
<li>👩‍💻
React 화면 개발, 프로젝트 리팩토링, 배포</li>
</ul>
</li>
</ul>
<hr>
<h3 id="기술스택">기술스택</h3>
<ul>
<li>언어<ul>
<li>Java</li>
<li>Mysql</li>
<li>JavaScript</li>
</ul>
</li>
<li>프레임워크<ul>
<li>Spring Boot</li>
</ul>
</li>
<li>라이브러리<ul>
<li>JPA</li>
<li>Lombok</li>
<li>Jasypt</li>
<li>React</li>
</ul>
</li>
</ul>
<hr>
<h1 id="필요하면-만들자">필요하면 만들자</h1>
<h2 id="1-사건의-발단">1. 사건의 발단</h2>
<h3 id="1-1-프로젝트-관리협업-툴">1-1. 프로젝트 관리(협업) 툴</h3>
<p>최소 1년에 1개 이상 프로젝트를 진행하는 나에게 매번 프로젝트를 진행할 때 마다 Tool에 대한 아쉬움이 있었다. 그나마 Notion으로 거의 유사하게 사용을 했지만 일반 팀원에게 짜여진 구조에 대한 설명을 하고 있자니 여간 번거롭고 사용까지 잘 이어지기 어렵기 일쑤였다. 예컨데, 표가 아닌 데이터 베이스의 개념을 설명을 해야하고 컬럼들의 종류 등 더 나아가면 관계형까지 설명을 해야했다. 그래서 쉽게 접근이 가능하고 직관적인 서비스를 찾아보았다. 생각보다 퀄리티가 좋아보이는 서비스들이 많이 있었다. 기대에 차 열심히 알아보았지만 되려 &quot;기능이 많다&quot; == &quot;할 수 있는게 많다&quot; == &quot;자유도가 높다&quot; == &quot;일반 팀원이 어렵게 느껴한다&quot;로 이어지거나 소수 그룹에게 제공하는 무료 플랜이 존재하지 않았다. 그리고 매달 내야하는 인당 요금제는 상당히 부담스러운 상태였다.</p>
<h3 id="1-2-너무-비싸다">1-2. 너무 비싸다</h3>
<p>이 단점들을 보완하는 대책이 직접 만들면 다 해결된다. 인당 기준 매달 내야하는 구독료의 합보다 AWS의 서버 비용이 낮으며 원하는 기능을 원하는 만큼 만들 수 있으니 말이다. 하지만 개발하는데 소요되는 시간과 노동력이 관건이였다. 마침 JPA 스터디를 진행하고 있었고 스케일도 크지 않으니 훈련도 할 수 있고 적당한 먹잇감으로 보였다.</p>
<h2 id="2-타겟-분석">2. 타겟 분석</h2>
<h3 id="2-1-쉬워야-한다">2-1. 쉬워야 한다</h3>
<p>프로젝트에는 IT 전공자들 이외의 인원들도 많이 참여한다. 노션이 아직까지는 높은 자유도 때문인지 입문에 심리적 거부감(?)을 느끼는 인원들이 많았다. 이왕이면 쉽고 간단한 사용에 포커스를 맞춰야 했다. 그래서서비스를 설계할때 최대한 단순하고 필요한 것만 챙기려고 신경을 많이 썼다.</p>
<h3 id="2-2-업무와-회의의-순환">2-2. 업무와 회의의 순환</h3>
<p>굳이 노션을 벗어나야했던 큰 이유가 일을 하는 흐름을 잘 반영할 수 없었다는 것인데, 우리 크루에서 일하는 스타일은 이렇다. 아이디어, 브리핑, 피드백, 컨펌, 투표, 의사결정 등 어떤 회의이던지와 상관없이 회의를 통해 새로운 업무들이 발생한다. 그 업무들은 다시 회의를 통해 기록되어 마무리 지어진다. 그러기 위해서는 회의와 업무간의 참조가 필요하고, 이렇게 하나의 업무 흐름이 완성된다. 업무 내용을 파악하는 것도 용이해진다. 이 과정이 어렵지 않아야하는데 노션은 아쉽게도 대체 수단이 되지 못 했다. 그래서 이번 서비스에서는 손쉽게 업무와 회의를 참조하는 기능이 추가되었다.</p>
</br>
</br>
</br>

<h1 id="익숙하지만-낯설다">익숙하지만, 낯설다.</h1>
<h2 id="1-기술적-변화">1. 기술적 변화</h2>
<p>이전까지는 MVC 패턴을 준수하는 Spring Framework를 사용하며 Controller에서 Data를 Model에 담아 JSP로 전달했다. 하지만 JPA 스터디 직후였고 새로운 개발 환경에서 작업을 하게 되며 겪은 차이점들을 적어보려고 한다.</p>
<h3 id="1-1-servlet-➡-rest-api">1-1. Servlet ➡ REST API</h3>
<p>이전에는 서버에서 너무 많은 역할을 하고 있었다. DB와 연결을 통해 Data를 주고 받으며, 서버 자체에서 Data를 가공하는 로직의 연산도 하며, 화면의 흐름제어와 동적 페이지 렌더링까지 웹 전반의 거의 모든 일을 도맡아 해왔다. 그런데 JPA를 사용하며 HTTP 프로토콜을 이용한 REST API 서버는 화면과 관련된 영역을 프론트 서버로 완전히 논리적으로 분리시켜놓은 듯 했다. 데이터의 입력/출력, 필요한 형태로 데이터를 정제 및 가공하는 역할만 오롯이 맡게된 것이다. 명확하게 WAS 서버와 클라이언트를 분리한 덕분에 멀티 플랫폼에도 대응하기 유리해졌다. 하지만 프론트 작업은 WEB 서버를 통해 별개로 관리 해야해서 성능상으로 이점을 가져오지는 못 한다고 한다. </p>
<h3 id="1-2-mybatis-➡-jpa">1-2. MyBatis ➡ JPA</h3>
<p>Mybatis는 Mapper에 일일히 사용할 쿼리들을 직접 작성하고 객체와 매핑하여 SqlSession에서 호출해서 사용해야 했다. 웹 개발을 하며 제일 지긋지긋한 시간이였던 것 같다. 그리고 의미없이 반복적인 작업을 해야하는 부분이였다. Spring Data JPA를 사용하면 이 시간들이 말끔히 사라진다. 기본적인 CRUD는 이미 자동으로 설정되어있어 이 구간을 생략할 수 있다. 그리고 이외의 쿼리는 쿼리 메소드를 이용하여 아주 쉽게 네이밍룰만으로도 추가할 수 있다. 더 복잡한 쿼리라면 JPQL을 사용하면 된다. 그리고 다이나믹하게 쿼리를 구현해야한다면, QueryDSL을 사용할 수 도 있다. 이 부분에서도 프로젝션을 이용해 엔티티 자체를 조회하거나 객체를 접근하듯이 엔티티의 요소들을 접근할 수도 있다. 말도 안되는 혁신이다. 영속성 컨텍스트를 잘 이해하고 나면 말이다.</p>
<h3 id="1-3-spring-➡-spring-boot">1-3. Spring ➡ Spring Boot</h3>
<p>Spring Boot는 Spring의 상위 버전이라기 보다 REST API를 개발하는 프로젝트를 위한 전용 무기같은 느낌이였다. 그래서 REST API 개발에 최적화되어 생략된 기능들만큼 설정도 줄어들었고, 그마저도 중요도가 높지 않은 셋팅을 자동으로 구성해주었다. Dependency도 &#39; spring-boot-starter-* &#39;를 제공하고 있어 추가하면 메이븐이나 그래들을 통해 필요한 라이브러리들을 자동으로 추가해준다. Spring 공식 사이트의 이니셜라이저를 하면 바로 실행이 가능한 코드를 제공해주기도 한다. 프로젝트를 셋팅하는 일이 엄청나게 간소화되어 시간 절약할 수 있어 좋았지만, 차이를 상세하게 알지 못하는 만큼 찜찜함도 가지고 있어서 나중에 더 공부 하는 시간을 가져야 겠다.</p>
<h3 id="1-4-maven-➡-gradle">1-4. Maven ➡ Gradle</h3>
<p>Maven은 외부 라이브러리를 손쉽게 네트워크를 이용해 받아줄 뿐만 아니라, 프로젝트의 Clean, Build, Site 등 전체적인 라이프 사이클을 관리하는 도구이다. 그리고 Gradle은 빌드 배포 도구로서 라이브러리 관리, 프로젝트 관리, 의존성 관리를 도와준다. 큰 차이점은 XML형태의 프로젝트 환경설정(POM, Project Object Model)로 Maven은 작성해야하고, Gradle은 Groovy라는 별도의 스크립트 언어를 사용해 동적인 환경설정이 가능하다고 한다. 가장 크게 느낀점은 속도가 메이븐에 비해 빠르며 스크립트가 매우 직관적이라는 것이 인상깊게 남았다. </p>
<h2 id="2-관점적-변화">2. 관점적 변화</h2>
<h3 id="2-1-생산적-개발을-위한-위임">2-1. 생산적 개발을 위한 위임</h3>
<p>개발을 하다보면 마법같이 시간과 노력을 단축시키는 장치들이 있다. 반복문과 어노테이션같은 경우 그렇다. 그렇게 효율과 생산적인 것에 비중을 두는 직업군이고 그런 환경을 조성하게 위해서도 많은 개발자들이 일을 하는데 이상하리만큼 단순 노동이 잦을 때도 많았다. 그런데 점점 그런걸 보완하는 대안들이 생기고 있다. 예를 들어 이번 프로젝트에서는 MyBatis에서 쿼리를 작성하는 것을 JPA를 사용하면서 단순반복을 줄인게 가장 크고, Spring Framework의 Bean을 이용한 DI도 그렇다. 그리고 아직 공부를 해보지는 않았지만 DDD를 도입한 프로젝트를 본적이 있는데 중복 로직을 방지하는 등 유지보수의 이점도 있고 불필요한 작업들이 줄어들어 있었다.  </p>
<p>다양한 방법으로 반복되는 일련의 작업들을 위임한다는 발상, 그것을 위해 한층 더 치밀하게 설계하며 핵심 로직에 더 집중한다. 백엔드의 묘미가 강화된 것 같다. 같은 결론을 두고도 더 나은 설계적, 운용적, 효율적인 방법을 채택하게 위해 항상 첨예하게 고민한다. 무너지지 않는 모래성을 보는 것 같다. 너무 멋진일이다.</p>
<h3 id="2-2-책임자의-역할">2-2. 책임자의 역할</h3>
<p>중요한 것, 필요한 것에 집중할 수 있게 된 대신 그에 따라오는 책임도 있다. 시스템을 운용하는 관리자적 설계가 필요해졌다. 로직을 고안할 때도 단순히 기능 범위를 넘어섰기 때문에 넓은 전문성을 요하게 됐고, 설계와 매핑의 중요도가 더 높아졌다. JPA를 예로 들면 엔티티와 테이블을 정확하게 매핑을 해야한다. 연관관계를 매핑할때도 미리 빈도를 잘 고려해 단방향과 양방향을 결정해야한다. 최적화를 위한 영속성 컨텍스트의 특징들을 모른다면 골치아픈 상황에 처할 수도 있다. 숲도, 나무도 모두 중요해졌다. </p>
<h1 id="마무리">마무리</h1>
<p>회고를 작성하다보니 그때는 잘 못느꼈지만 정말 도움이 많이 됐구나라는 생각이 다시금 든다. 아이디어가 목적이 아니라 JPA 스터디였다 보니 학습에 포커스를 맞추기 수월했다. 처음에는 직관적이지 않다고 생각했다. 명확하지 않다고 생각해서 찜찜했고 낯선데 어렵다고 느꼈다. 그런데 프로젝트 셋팅하고 엔티티 매핑이 끝나고 로직을 구현하고 완벽하게 API로 분리해서 Postman 으로 테스트하는 과정이 신기하고 즐거웠다. 아직 공부해야할 것들이 많지만 그래도 완전히 모르던 미지의 영역에서 탈출시켰다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Aloha Coupon, 커플 쿠폰 앱, 2021]]></title>
            <link>https://velog.io/@noly-poly/aloha-coupon</link>
            <guid>https://velog.io/@noly-poly/aloha-coupon</guid>
            <pubDate>Fri, 02 Dec 2022 10:50:53 GMT</pubDate>
            <description><![CDATA[<h1 id="개요">개요</h1>
<h2 id="서비스">서비스</h2>
<h3 id="미리보기">미리보기</h3>
<p><img src="https://velog.velcdn.com/images/noly-poly/post/8a5172b6-d316-4702-8b40-72b381996b35/image.PNG" alt=""></p>
<hr>
<h3 id="소개">소개</h3>
<ul>
<li><strong>서비스 명</strong>: Aloha Coupon, 알로하 쿠폰</li>
<li><strong>서비스 주제</strong>: 커플 쿠폰 서비스</li>
<li><strong>서비스 플랫폼</strong>: iOS App</li>
</ul>
<hr>
<h3 id="핵심기능">핵심기능</h3>
<ul>
<li>두 명의 사용자가 커플로 연동할 수 있다.</li>
<li>커플 쿠폰북에 원하는 쿠폰을 추가할 수 있다.</li>
<li>쿠폰북에서 골라 상대에게 쿠폰을 발급할 수 있다.</li>
<li>보유하고 있는 쿠폰을 사용하면 상대에게 알림이 전송된다.</li>
</ul>
<hr>
<h3 id="스토리-보드">스토리 보드</h3>
<p><img src="https://velog.velcdn.com/images/noly-poly/post/9cee2f2d-3cfe-44c4-b827-79651abfb715/image.png" alt=""></p>
</br>

<h2 id="프로젝트">프로젝트</h2>
<h3 id="규모">규모</h3>
<ul>
<li><p><strong>기간</strong>: 3달</p>
<ul>
<li>전체 기간</li>
</ul>
<p>20.12.28 ~ 21.03.18</p>
</li>
<li><p><strong>인원</strong>: 3명</p>
<ul>
<li><strong>Noly-Poly</strong>✋ <code>PM</code>
네이티브 iOS App 개발</li>
<li><strong><a href="https://aejeong.com/">Aejeong</a></strong>👩‍💻
도메인 및 API 서버 구현<ul>
<li><a href="https://www.notion.so/egoistcrew/EGO-2022-0a7eddea193e42728f21f33e16991ff9">FCM PUSH(IOS) - Java HTTP v1 사용하여 PUSH 알림 적용하기</a></li>
</ul>
</li>
<li><strong>Whaler</strong>👨
브랜딩, 마케팅</li>
</ul>
</li>
</ul>
<hr>
<h3 id="기술-스택">기술 스택</h3>
<ul>
<li><strong>언어</strong><ul>
<li>Swift/SwiftUI</li>
<li>Java</li>
<li>MySql</li>
</ul>
</li>
<li><strong>프레임워크</strong><ul>
<li>Spring Boot</li>
</ul>
</li>
<li><strong>API</strong><ul>
<li>Social Login(Kakao, Google, Apple)</li>
<li>Firebase FCM Notification</li>
<li>Apple In-App Purchase API</li>
</ul>
</li>
<li><strong>라이브러리</strong><ul>
<li>Alamofire</li>
<li>SwiftyJSON</li>
<li>JPA</li>
<li>QueryDsl</li>
<li>Lombok</li>
<li>okhttp3</li>
<li>Jasypt</li>
</ul>
</li>
</ul>
</br>
</br>
</br>

<h1 id="기획">기획</h1>
<h3 id="1-커플-쿠폰이란">1. 커플 쿠폰이란?</h3>
<p>나와 여자친구는 &#39;커플 쿠폰&#39;이라는 개념을 도입해 장기 연애 중 일어나는 거스러미를 유연하게 대처하고 있었다. 예를들어 약속에 지각을 했을때 무조건 용서를 해주어야하는 &#39;지각 용서권&#39;, 보고 싶다고 호출을 하면 모든 일을 제쳐두고 달려가야 하는 &#39;소환권&#39;, 커피나 밥을 사주는 쿠폰 등 다양한 상황에 맞춰 쿠폰을 서로 주고 사용하는 식이다. 상대에게 화가 나는 일이 생겼을때 용서해줄테니 쿠폰을 달라고 <del>협박</del> 협상을 할 수도 있다. </p>
<hr>
<h3 id="2-팀-프로젝트로-출발">2. 팀 프로젝트로 출발</h3>
<p>이 커플 쿠폰 제도는 참 좋은데 단점이 있다. 쿠폰을 사용하지 않고 무한정 쟁여놓아 부담을 높이거나 구두로 진행되다 보니 혼동하는 경우도 잦았다. 그래서 서비스로 만들어 관리할 수 있으면 편리할 것 같아 이번 팀 프로젝트로 진행되었다.</p>
<hr>
<h3 id="3-웹이-아닌-앱-개발">3. 웹이 아닌 앱 개발</h3>
<p>이 서비스의 핵심 기능은 쿠폰을 잘 보관하고 쿠폰을 사용했을때 상대에게 알림(푸시)가 가는 것이다. 그런데 알림 기능을 구현하려면 모바일 웹에서는 불가능 했고 팀에는 앱을 개발할 수 있는 개발자가 없었다. 고민 끝에 Objective-C까지는 아니더라도 전부터 그나마 Swift에 관심이 있었던 내가 맡기로 했다.</p>
</br>
</br>
</br>

<h1 id="개발">개발</h1>
<h2 id="1-속성으로-학습하기">1. 속성으로 학습하기</h2>
<h3 id="1-1-swift와-swiftui">1-1. Swift와 SwiftUI</h3>
<p>책과 온라인 클래스를 알아 보다가 인프런에서 커리큘럼에 JSON 통신과 여러 예제들도 포함된 <a href="https://www.inflearn.com/course/swift-ui-ios14/dashboard">SwiftUI - iOS14 퍼펙트 가이드</a>를 찾아서 열심히 수강했다. 강의가 다 끝나서야 알게된 것이 Swift와 SwiftUI는 엄연히 다른 언어였다. Swift가 Server의 Java라면 SwiftUI는 JavaScript 기반의 jQuery를 쓰는 차이와 비슷했다. 그래서 데이터 모델과 여러 API를 사용하기 위해 Swift도 공부를 해야했는데 <a href="https://www.inflearn.com/course/Swift-KXcoding/dashboard">iOS 개발을 위한 swift5 완벽 가이드</a>를 참고하며 구글링하며 프로젝트를 진행했다. 쉽고 객체지향적이며 괜찮은 언어라고 생각되는데 Swift와 관련해서 참조 자료가 아직까지 너무 부족해서 문제가 생겼을때마다 애를 먹었던 것이 기억에 남는다.</p>
</br>

<h2 id="2-api">2. API</h2>
<h3 id="2-1-api-서버와-통신">2-1. API 서버와 통신</h3>
<p><a href="https://www.postman.com/">Postman</a>으로 API를 테스트하며 응답결과에 맞춰 연결해나갔다. 처음에는 App에서 API 서버로 요청하기 위해 Swift로만 사용했었다.</p>
<pre><code class="language-swift">class WebService{
    func getCoupon(completion: @escaping ([CouponVO]) -&gt; Void) {
        guard let url = URL(string: &quot;&quot;) else {
            return
        }

        URLSession.shared.dataTask(with: url) { (data, response, err) in
                guard let hasData = data else {
                    return
                }

             let coupons = try! JSONDecoder().decode([CouponVO].self, from: hasData)
             completion(coupons)
        }
    }.resume()

}</code></pre>
<p>그러다가 직관성과 HTTP 메소드를 편하게 사용하기 위해 라이브러리를 찾아보았다. Cocoapods을 통해서 라이브러리들을 install 하였고, Alamofire를 사용한 코드로 대체하여 통신하고 받아온 JSON은 SwiftyJSON으로 파싱하여 사용했다.</p>
<pre><code class="language-swift">class WebService{
    func getCoupon(){
        let url = &quot;&quot;
        AF.request(
            url,
            method: .get,
            parameters: nil,
            encoding: URLencoding.default,
            header: [&quot;Content-Type&quot;:&quot;application/json&quot;, &quot;Accept&quot;:&quot;application/json&quot;]
            )
        .validate(statusCode: 200..&lt;300)
        .responseJSON{ (json) in
            print(json)
        }
    }
}</code></pre>
<hr>
<h3 id="2-2-소셜-로그인-api">2-2. 소셜 로그인 API</h3>
<pre><code class="language-swift">import SwiftUI
import KakaoSDKCommon
import KakaoSDKAuth
import Firebase
import GoogleSignIn


class AppDelegate: NSObject, UIApplicationDelegate, GIDSignInDelegate {
    // 카카오 로그인
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -&gt; Bool {
        print(&quot;didFinishLaunchingWithOptions called!&quot;)

        KakaoSDKCommon.initSDK(appKey: &quot;&quot;, loggingEnable:true)

        return true
    }

    // 구글 로그인
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -&gt; Bool {
        print(&quot;didFinishLaunchingWithOptions called!&quot;)

        FirebaseApp.configure()

        GIDSignIn.sharedInstance().clientID = FirebaseApp.app()?.options.clientID
        GIDSignIn.sharedInstance().delegate = self

        return true
    }


    func sign(_ signIn: GIDSignIn!, didSignInFor user: GIDGoogleUser!, withError error: Error!) {
        guard let user = user else{
            print(error.localizedDescription)
            return}

        let credential =
            GoogleAuthProvider.credential(withIDToken: user.authentication.idToken, accessToken: user.authentication.accessToken)

        Auth.auth().signIn(with: credential){ (result, err) in
            if err != nil{
                print((err?.localizedDescription)!)
                return
            }
            // SocialSession
            signTemp.set(platform:&quot;google&quot;,id:(result?.user.email)!)
            print((result?.user.email)!)
        }
    }
}</code></pre>
<p>API 문서에 설명된 환경과 달랐는데 참조 자료가 나오질 않았다. 한참을 해매다가 결국에 Main Struct에 Adaptor을 이용해 AppDelegate를 연결해주어 해결했다. 테스트에 성공을 하고 <strong>SocialSession 객체</strong>를 만들어 카카오, 구글, 애플 로그인을 한번에 컨트롤 할 수 있도록 했다. </p>
<blockquote>
<p>애플 로그인은 화면에서 구현을 하게끔 되어있었다.</p>
</blockquote>
<pre><code class="language-swift"> SignInWithAppleButton(.signIn,
     onRequest:{ (request) in
        request.requestedScopes = [.fullName, .email]
    }, onCompletion:{ (result) in
        switch result{
            case .success(let authorization) :
                if let appleIDCredential = authorization.credential as? ASAuthorizationAppleIDCredential {
                    let userId = appleIDCredential.user
                    let identityToken = appleIDCredential.identityToken
                    let authCode = appleIDCredential.authorizationCode
                    let email = appleIDCredential.email
                    let givenName = appleIDCredential.fullName?.givenName
                    let familyName = appleIDCredential.fullName?.familyName
                    let state = appleIDCredential.state
                    socialSession.set(state: true, id: userId)
                    isLogin.toggle()
                }
            break
        case .failure(let error) :
             break
        }
    }
)</code></pre>
<hr>
<h3 id="2-3-푸시-api">2-3. 푸시 API</h3>
<p>FCM API를 이용해 알림 기능을 개발하는데 앱 자체에서 요청과 응답을 처리하려고 했으나 로컬 푸시를 성공시키지 못했다. 그래도 원격으로 서버를 한번 거쳐 <strong>앱 -&gt; 서버 -&gt; FCM -&gt; 앱</strong>으로 구현을 마쳤다. 알고보니 하필 업데이트 사이즈가 컸던 최신 버전의 Swift를 사용하게 되었는데 그래서 같은 환경의 사례를 찾아볼 수 없었다. 최신 버전의 언어를 사용하면서 겪는  애로사항을 알게된 것 같다.
<img src="https://velog.velcdn.com/images/noly-poly/post/f209d29a-ee32-474a-9e1f-2fa9c44854c4/image.png" alt=""></p>
<h1 id="앱스토어-등록">앱스토어 등록</h1>
<h2 id="app-store-connect">App Store Connect</h2>
<h3 id="개발자-등록">개발자 등록</h3>
<p>처음으로 해야할 것은 애플 개발자 등록이였다. 애플 개발자 프로그램의 회비는 무려 연 99달러로 한화 129,000원인 셈이라 작은 금액은 아니였지만 꼭 해야하는 절차였다. 개인의 경우 크게 어려움 없이 신청하고 몇 일이 지난 뒤 승인받을 수 있었다.</p>
<hr>
<h3 id="app-upload">App Upload</h3>
<p>예전에는 더 복잡했었다고 하는데 간단해졌다. xCode에서 Archive해둔 App을 App Store Connect로 바로 Upload 할 수 있었다. 버전 업데이트할때마다 과정을 반복했다. 처음에 애플 개발자 사이트에서 Certificates, Identifiers &amp; Profiles, Provisioning 들을 등록해야하는데 사이트가 리뉴얼이 되면서 카테고리 분류도 달라져있어서 적당히 잘 찾아서 해야했다.</p>
<hr>
<h3 id="testflight">TestFlight</h3>
<p>나는 바로 테스트 해볼 수 있었지만 팀원들의 경우 App을 테스트하려면 이메일 인증을 거쳐 사용자를 등록해준다음 TestFlight 앱을 앱스토어에서 다운 받으면 앱 내 리스트에서 베타 버전의 App을 설치하는 절차가 필요했다. 개발자 팀원들은 어려움이 없었지만 일반 팀원들은 복잡하게 느꼈다고 한다.
<img src="https://velog.velcdn.com/images/noly-poly/post/b95c954f-13eb-4b36-9161-1c84656f9d16/image.jpeg" alt=""></p>
</br>

<h2 id="appstore-심사">Appstore 심사</h2>
<p>애플 앱스토어 심사가 깐깐하기로 유명했기에 리젝 사례들을 찾아 미리 검토를 열심히 했다. 최근 몇년사이 기간도 짧아지고 개선됐다고 한다.</p>
<blockquote>
<h3 id="tip">TIP</h3>
<ul>
<li>애플 심사원은 한글을 읽을 수 있다.</li>
<li>코드를 뜯어보지 않고 표면적인 기능을 심사한다.</li>
<li>앱이 복잡하면 심사에 걸리는 시간이 길어진다.</li>
<li>보통 2일 내에 승인, 리젝이 나는 경우가 많다.</li>
<li>미국 휴일을 포함하는 경우 심사 기간이 매우 길어진다.</li>
</ul>
<h3 id="reject-case">REJECT CASE</h3>
<ol>
<li><p><strong>로고, 저작권 도용</strong>
샘플 데이터, 아이콘, 스크린샷에 주의한다.</p>
</li>
<li><p><strong>앱 빌더 사용</strong>
다른 사업체의 앱을 하나의 계정으로 관리하는 것은 불가능하다.</p>
</li>
<li><p><strong>테스트용 아이디</strong>
애플 심사 직원은 회원가입을 하지 않는다.</p>
</li>
<li><p><strong>회원가입 필수 필드</strong>
GDPR(개인정보보호법)에 따라 표면적으로 연관성이 없다면 설득해야하고 보통 리젝된다.</p>
</li>
<li><p><strong>ip 노출</strong>
앱을 사용하는 도중 어떤 이유로든 ip가 노출되어선 안되고 반드시 도메인이 연결되어 있어야한다.</p>
</li>
<li><p><strong>테스트 콘텐츠 노출</strong>
차라리 아무 컨텐츠가 없는건 괜찮지만 test와 같은 개발을 위한 단어가 들어있으면 리젝 사유이다. </p>
</li>
</ol>
<h3 id="attention">ATTENTION</h3>
<ul>
<li><p><strong>통신</strong></p>
<ol>
<li>HTTPS 프로토콜을 준수한다.</li>
</ol>
</li>
<li><p><strong>소셜 로그인</strong></p>
<ol>
<li>일반 로그인 혹은 게스트 로그인이 필요하다.</li>
<li>로그인 이외에 소셜 기능을 사용한다.</li>
<li>애플 로그인은 필수로 탑재한다.</li>
</ol>
</li>
<li><p><strong>완성도</strong></p>
<ol>
<li>오류가 발생하지 않는다.</li>
<li>앱으로 만들지 않아도 될 정도의 단순한 기능 구성이 아니다.</li>
<li>형편없는 서비스 디자인이 아니다.</li>
</ol>
</li>
<li><p><strong>결제</strong></p>
<ol>
<li>인앱으로 구입해야하는 디지털 재화를 다른 방식(쿠폰과 같은)으로 제공하지 않는다.</li>
<li>인앱으로 구입해야하는 디지털 재화를구입해서 상대방에게 선물하는 기능을 제공하지 않는다.</li>
<li>초대, 리뷰에 대한 리워드를 주지 않는다.</li>
</ol>
</li>
<li><p><strong>스크린샷</strong></p>
<ol>
<li>아이폰이 아닌 형태의 스마트폰이 그려져 있는 앱 스크린샷을 제출하지 않는다.</li>
</ol>
</li>
</ul>
</blockquote>
<p><img src="https://velog.velcdn.com/images/noly-poly/post/7b9fdac2-fef8-4f08-b85a-e6e79ef19061/image.png" alt=""></p>
<p>하지만 리젝을 피할 수 없었는데 말도 안되는 이유는 아니였고 양식에 어긋난 부분들을 조금씩 수정했다. 그리고 로그인을 하고 커플 연동을 해야 완전히 서비스를 이용해볼 수 있었는데 임시 커플 연동 코드를 기재해서 테스트 해보실 수 있도록 하고 재심사에 통과했다.</p>
</br>

<h2 id="알로하-쿠폰-플러스">알로하 쿠폰 플러스</h2>
<h3 id="인앱결제-기능">인앱결제 기능</h3>
<p>앱의 반응도 괜찮았고 구독방식 플러스 기능을 추가하자고 팀의 의견이 모여서 인앱결제 기능을 개발하는데 레퍼런스 자료가 한, 두개 정도밖에 찾을 수가 없었고 심지어 지금 개발환경과 일치하지도 않아서 분석하고 여러 시도를 하는데 애를 먹었다.</p>
<p><img src="https://velog.velcdn.com/images/noly-poly/post/63e4e084-7cf3-42ed-bcc2-11bfc75db7dd/image.png" alt=""></p>
<hr>
<h3 id="운영-배포">운영 배포</h3>
<p>1.0 버전이 먼저 배포가 된 후 결제 기능이 추가된 버전을 배포해야 했는데 수익이 생기는 일이다 보니 사업자와 통신판매업이 필요해 이것을 등록하느라 보름정도 소요됐던 것 같다. 그런데 문제가 생겼다. 테스트 환경에서는 결제 기능이 잘 됐었지만 배포된 앱에서는 제대로 작동하지 않는 것이였는데, 이게 앱에서 문제가 있는건지 Connect 설정에 문제가 있는건지 알 수 있는 단서가 없었다. 결국 해결하지 못 하고 프로젝트를 종료하게 되었다. 너무 아쉽다.</p>
<p><img src="https://velog.velcdn.com/images/noly-poly/post/5318c537-db35-4482-9aee-9ce2fd35e936/image.png" alt=""></p>
</br>
</br>
</br>

<h1 id="마치며">마치며</h1>
<p>JAVA 언어 기반의 웹 개발에 익숙해있던 나에게 새롭지만 막막한 모험이였던 프로젝트였다. 처음 써보는 언어와 개발 및 테스트 환경과 툴에 배포 또한 다 해매고 찾아보았어서 아주 특별한 경험으로 남을 것 같다. 또 써보고 싶다.
<img src="https://velog.velcdn.com/images/noly-poly/post/cd21658e-af11-4c2f-a0a1-27374164decc/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[GoLa, 자동 인원 선별 서비스, 2020]]></title>
            <link>https://velog.io/@noly-poly/gola</link>
            <guid>https://velog.io/@noly-poly/gola</guid>
            <pubDate>Thu, 24 Nov 2022 05:13:08 GMT</pubDate>
            <description><![CDATA[<h1 id="개요">개요</h1>
<h2 id="서비스">서비스</h2>
<h3 id="미리보기">미리보기</h3>
<p><img src="https://velog.velcdn.com/images/noly-poly/post/bb5d1d5c-e811-4eae-b33b-16968297af16/image.png" alt=""></p>
<ul>
<li><a href="http://gola.egoist.im/">Demo</a></li>
<li><a href="https://github.com/JISU-YANG/2020_GoLa">Github</a></li>
</ul>
<hr>
<h3 id="소개">소개</h3>
<ul>
<li><strong>서비스 명</strong>: GoLa, 골라</li>
<li><strong>서비스 주제</strong>: 인원 선별 서비스</li>
<li><strong>서비스 플랫폼</strong>: 모바일 웹 서비스</li>
</ul>
<hr>
<h3 id="핵심-기능">핵심 기능</h3>
<ul>
<li>n명의 벌칙자를 정하는 미니게임을 할 수 있다.</li>
<li>전체 인원을 원하는 팀의 수만큼 랜덤하게 배정한다.</li>
<li>토너먼트 대진표를 자동으로 구성할 수 있다.</li>
</ul>
</br>

<h2 id="프로젝트">프로젝트</h2>
<h3 id="규모">규모</h3>
<ul>
<li><p><strong>기간</strong>: 3주</p>
<ul>
<li>20.09.14 ~ 20.10.05</li>
</ul>
</li>
<li><p><strong>인원</strong>: 2명</p>
<ul>
<li>🤚: <strong>PM</strong>
기획, UI/UX, 프로젝트 배포<ul>
<li>팀 나누기</li>
<li>사다리 타기</li>
<li>주사위 배틀</li>
</ul>
</li>
<li>👨‍💻:<ul>
<li>토너먼트 대진표</li>
<li>러시안 룰렛</li>
<li>제비뽑기</li>
<li>병돌리기</li>
</ul>
</li>
</ul>
</li>
</ul>
<hr>
<h3 id="기술-스택">기술 스택</h3>
<ul>
<li><p><strong>언어</strong></p>
<ul>
<li>JavaScript</li>
<li>HTML/CSS</li>
</ul>
</li>
<li><p><strong>API</strong></p>
<ul>
<li>Google Charts</li>
</ul>
</li>
<li><p><strong>라이브러리</strong></p>
<ul>
<li>Bootstrap</li>
<li>jQuery</li>
<li>Google Fonts</li>
</ul>
</li>
</ul>
</br>
</br>
</br>

<h1 id="keep">Keep</h1>
<h2 id="1-필요한-것-정복하기">1. 필요한 것 정복하기</h2>
<h3 id="1-1-git을-공부하게된-이유">1-1. Git을 공부하게된 이유</h3>
<p>국비과정 중에 Git에 대한 이론적인 개념만 가볍게 배우고 넘어갔다. 사실 아직까지도 의문이다. 과정에서 왜 Git을 써보게 하거나 디테일한 부분에 대한 이야기 그리고 개발자에게 얼마나 중요한지에 대해 알려주지 않았다. 그래서 이제서야 채용 공고 혹은 먼저 취업한 동료들과 여자친구의 경험을 통해 거의 모든 곳에서 사용한다는 것을 알게되어 충격이었다.</p>
<hr>
<h3 id="1-2-학습을-위한-주제선정">1-2. 학습을 위한 주제선정</h3>
<p>이번 프로젝트는 Github를 공부하기 위해 시작했다. 그래서 프로젝트의 주제나 퀄리티는 중요하지 않았고, 프로젝트의 한 싸이클을 Github와 함께 경험해보는 것이 중요했기 때문에 작은 스케일의 주제를 선정하는게 효율적인 선택이었다. 브레인 스토밍 끝에 해리포터 속 마법의 분류모자(호그와트의 신입생들의 기숙사를 분류해주는 모자)를 모티브 삼았고 인원 배정, 미니 게임을 기능으로 삼았다.</p>
<p><img src="https://velog.velcdn.com/images/noly-poly/post/14b2d0a5-884a-4b1f-b0bd-239dfc51c6be/image.png" alt=""></p>
<hr>
<h3 id="1-3-온라인-클래스로-학습하기">1-3. 온라인 클래스로 학습하기</h3>
<p>쉽게 구글링으로 접할 수 있는 정보로는 약간 아쉬움이 남아, 인프런에 있는 온라인 클래스를 참고했다. <a href="https://www.inflearn.com/course/git-and-github/dashboard">Git과 GitHub 시작하기</a>를 팀원과 같이 수강했는데 무료 강의에다가 강의 시간이 2시간 20분 남짓이며 터미널을 고집하지 않고 <a href="https://www.sourcetreeapp.com/">Sourcetree</a>라는 툴을 사용해 거부감이 낮았다. 개념을 익히기에 충분했다.</p>
<hr>
<h3 id="1-4-프로젝트로-실습하기">1-4. 프로젝트로 실습하기</h3>
<blockquote>
</blockquote>
<ol>
<li>원본 레포지토리를 fork하고 클라이언트로 clone을 받는다.</li>
<li>작업을 하고 commit을 하고 push를 하고 </li>
<li>upstream으로 Pull Request 하고</li>
<li>merge한다.</li>
</ol>
<p>위의 과정으로 작업을 진행했다. 실무 환경을 잘 알고있던 여자친구에게 과정에 대한 조언을 받았고 흔히 일어나던 Github로 인한 문제는 크게 없이 프로젝트를 진행할 수 있었다. 하지만 커밋 단위에 대한 아쉬움이 남는데 기준을 잘 몰랐던 것 같다는 생각이 들었다. 개인적으로 커밋 단위가 더 잘게 나누어져야하지 않나 생각이 들었다. 그렇게 하지 않았다 보니 되돌려야하는 경우가 생겼을때 곤란하기도 했었고 작업 내용들을 직관적으로 확인하기 불편했던 적이 몇 번 있었다. 그래서 가이드 기준같은게 있나 찾아보니 오히려 이 부분에 대한 <a href="https://okky.kr/articles/337818?note=1089865">다양한 의견</a>을 찾아볼 수 있었다.</p>
</br>

<h2 id="2-중요한-것에-집중하기">2. 중요한 것에 집중하기</h2>
<h3 id="2-1-db와-서버-사용하지-않기">2-1. DB와 서버 사용하지 않기</h3>
<p>이번 프로젝트는 DB를 필요로 하지 않기 때문에 서버에서 할 기능들도 프론트에서 js로 처리하는 것이 좋겠다고 생각했다. 그러면 따로 tomcat를 필요로 하지 않기에 유지비용을 줄이는 데 이점이 있기 때문이다. 그래서 &quot;오히려 좋아&quot; 마인드로 요새 급 부상하고 있는 JavaScript만을 사용해보기로 했다. 에러 추적이 Java에 비해 디테일하지 않다는 점과 기본 문법도 Java와 다르게 조금 더 추상적이라는 점을 제외하고는 이점이 더 크게 보였던 것 같다. <del>아직 내 수준이 그렇게 높지 않아서 그런 것 일수도 있다.</del></p>
<hr>
<h3 id="2-2-웹앱-템플릿-사용하기">2-2. 웹앱 템플릿 사용하기</h3>
<p>모바일 웹으로 만들어야 했는데 템플릿을 구매하여 시간을 단축시켰다. 템플릿을 알아보는 과정에서 <a href="https://medium.com/iquii/progressive-web-app-pwa-what-they-are-pros-and-cons-and-the-main-examples-on-the-market-318f4538c670">PWA</a>를 중요하게 생각했다. 가장 큰 특징이 모바일 브라우저 앱의 더보기에서 &#39;홈화면에 추가&#39;를 하게 되면 일반적인 앱과 똑같은 환경처럼 사용할 수 있다는 것이기 때문이다. <del>네이티브 앱처럼 알림 기능은 불가능하다.</del> 안드로이드 뿐만 아니라 ios에서도 가능하다. 템플릿의 컴포넌트들을 조합하고 기획에 맞춰 소스를 커스텀해서 프론트 구성을 빠른 시간에 수월하게 마쳤다.</p>
</br>
</br>
</br>

<h1 id="problem--try">Problem &amp; Try</h1>
<h2 id="1-코드-리뷰">1. 코드 리뷰</h2>
<h3 id="1-1-스타일과-협업">1-1. 스타일과 협업</h3>
<p>팀 프로젝트를 진행 하면 항상 가장 어렵다고 느끼는게 이 부분인데 개인마다 다른 프로그래밍 수준과 코드 스타일을 가지고 있다. 그래서 기준을 어디로 두어야 할지 모르겠다. 스타일에 대한 존중의 범위는 어디까지이며 만약 로직의 수준이 결과물에 영향을 미친다면 어디까지 용인이 가능한지 쉽게 기준을 정할 수 없다.</p>
<blockquote>
</blockquote>
<p>Q. &quot;왜 이렇게 코드를 작성했어요?&quot;
A. &quot;모르겠어요.&quot;</p>
<p>간단한 방법이 있는데 하드코딩을 해놓았거나 빙빙 돌아 로직을 처리한 경우</p>
<blockquote>
</blockquote>
<p>Q. &quot;이런 방법이 있는데 더 나을거에요.&quot;
A. &quot;알려준 방법으로는 어떻게 구현해야할지 모르겠다.&quot;</p>
<p>이런 상황이 종종 발생하곤 했다. 어떤 액션을 취해야할지 몰랐고 대처를 잘 하지 못 했다. 일단은 코드를 대신 작성하는 것은 서로에게 도움이 되지않기 때문에 하지 않기로 전에 다짐했었고 마땅한 해결책이 없어 그냥 진행토록 했다. 하지만 내가 PM이었고 팀 구성도 내가 했고 기능 분배도 참여했다. &#39;담당 기능을 줄이더라도 시간을 더 배분하자&#39;는 것이 이전 프로젝트에서 느낀점이었지만 적용하려 설득하는 것이 쉽지 않았다.</p>
<hr>
<h3 id="1-2-프로그램의-일관성">1-2. 프로그램의 일관성</h3>
<p>한 프로젝트의 결과물이 얼마나 일관성을 갖추어야 하는지. 그리고 일관성이 높은게 꼭 좋다고 할 수 있는지 정확하게 알지 못한다. 나의 완벽주의가 &#39;사자의 머리에 뱀의 꼬리를 가진 키메라&#39;를 못 마땅하게 생각한 것인지도 돌아보고 팀의 의욕 문제는 아니었는지 돌아보게 한다. 이 부분은 설계적인 부분에서 혹은 개발을 하다가 확실한 근거가 있는 경우에는 적극적으로 의견을 내보는 걸로 결론을 지었다.</p>
<hr>
<h3 id="1-3-코드-리뷰-문화">1-3. 코드 리뷰 문화</h3>
<p>프로젝트가 끝나고서야 &#39;코드 리뷰&#39;라는 문화를 알게 되었다. PR을 기점으로 서로의 코드를 고민해보고 피드백하는 과정인데 팀 프로젝트를 할 때에도 서로 성장할 수 있는 좋은 과정일 것 같다. 기회가 된 다면 빨리 경험해보고 싶다. </p>
<p>코드 리뷰에 참고하고 싶은 <a href="https://tech.kakao.com/2022/03/17/2022-newkrew-onboarding-codereview/">카카오 테크 포스팅</a>의 요약이다.</p>
<blockquote>
</blockquote>
<p><strong>코드 리뷰</strong>란 한 개발자가 코드를 작성하면 다른 개발자가 정해진 방법으로 피드백을 주고받는 과정을 말합니다. 이 과정을 통해 본인이 발견하지 못한 실수를 다른 사람이 발견하여 <strong>코드의 부작용</strong>(Side effect)과 <strong>오류</strong>를 조기에 대응할 수 있으며, 개발 내 정해진 <strong>컨벤션 규칙</strong>을 유지하고 <strong>기술 부채</strong>를 줄일 수 있습니다. 또한 여러 명의 개발자가 참여함으로써 문제 해결을 위한 <strong>기술 구현 방법론</strong>에 대해 공유하기도 합니다.</p>
<ol>
<li>왜 개선이 필요한지 이유를 충분한 설명해 주세요.</li>
<li>답을 알려주기보다는 스스로 고민하고 개선 방법을 선택할 수 있게 해주세요.</li>
<li>코드를 클린 하게 유지하고, 일관되게 구현하도록 안내해 주세요.</li>
<li>리뷰 과정이 숙제검사가 아닌 학습과정으로 느낄 수 있게 리뷰해 주세요.</li>
<li>리뷰를 위한 리뷰를 하지 마세요. 피드백 할 게 없으면 칭찬해 주세요.</li>
</ol>
</br>

<h2 id="2-답이-없는-알고리즘">2. 답이 없는 알고리즘</h2>
<h3 id="2-1-팀-나누기">2-1. 팀 나누기</h3>
<pre><code class="language-js">// arName : 전체 인원 리스트
// numTeam : 나눌 팀의 수
// return: array[팀][인원] 2차원 배열
function makeTeam(arName, numTeam) {
    arName = shuffle(arName);

    // 팀 설정
    let arrTeam = new Array(numTeam);
    for (let i = 0; i &lt; numTeam; i++) {
        arrTeam[i] = new Array(Math.ceil(arName.length / numTeam));
    }
    let x = 0,
        y = 0;
    for (let i = 0; i &lt; arName.length; i++) {
        if (i != 0 &amp;&amp; i % numTeam == 0) [x, y] = [0, y + 1];
        arrTeam[x++][y] = arName[i];
    }

    return arrTeam;
}
</code></pre>
<p>이 로직에서 중요한 것은 랜덤으로 원하는 팀의 수만큼 인원을 나누는 것이다. 다양한 방법이 떠올랐지만 나는 for문의 개수를 최대한 줄이고 싶었다. 그래서 2차원 배열을 컨트롤 하기 위해 2개의 변수로 핸들링했다.</p>
<hr>
<h3 id="2-2-사다리-타기">2-2. 사다리 타기</h3>
<p>처음 사다리의 선들을 만드는 로직을 짜는 것은 어렵지 않았다. 하지만 사다리 타기의 결과를 모르면 의미가 없다. 그래서 플레이어의 아이콘이, 생성된 사다리를 따라 결과로 이어지는 기능을 구현해야했는데 정말 어려웠다. 계속 로직을 뒤엎어야 했다. 그리고 화면으로 연결하는건 또 다른 문제였다. 
결국 화면의 요소들의 값을 고정하고 보이지 않는 div Element를 이용해서 성공을 했는데 과정은 이렇다.</p>
<ol>
<li><p>사다리를 만든다</p>
<pre><code class="language-js">function makeSadari(num) {
 const number = 2 * num + 1;
 let arSadari = new Array(number);

 for (let i = 0; i &lt; number; i++) {
     arSadari[i] = new Array(len);

     for (let j = 0; j &lt; len; j++) {
         arSadari[i][j] = 0;
         if (i % 2 == 1) {
             arSadari[i][j] = &#39;x&#39;;
         }
     }
 }

 // 랜덤 숫자 방향 입력
 let sfCnt = 1;
 let tempLine = 0;
 while (true) {
     let rdLine = (Math.floor(Math.random() * (num - 1))) * 2 + 2;
     if (tempLine == rdLine) continue;
     arSadari[rdLine][sfCnt] = Math.floor(Math.random() * 3 + 1);
     tempLine = rdLine;
     sfCnt++;
     if (sfCnt == len - 1) break;
 }
 return arSadari;</code></pre>
</li>
<li><p>경로를 분석한다.</p>
<pre><code class="language-js">function findSadari(arSadari, focusLine) {
 let x = focusLine * 2 - 1;
 let y = 1;
 let arDistance = new Array(len * 2);
 let tempDistance = &quot;&quot;;

 while (true) {
     if (arSadari[x - 1][y] == &#39;0&#39; &amp;&amp; arSadari[x + 1][y] == &#39;0&#39;) {
         tempDistance += 2;
         y++;
     } else {
         if (arSadari[x - 1][y] == &#39;1&#39;) {
             tempDistance += 4;
             x -= 2;
         } else if (arSadari[x - 1][y] == &#39;2&#39;) {
             tempDistance += 7;
             x -= 2;
         } else if (arSadari[x - 1][y] == &#39;3&#39;) {
             tempDistance += 1;
             x -= 2;
         } else {
             if (arSadari[x + 1][y] == &#39;1&#39;) {
                 tempDistance += 6;
                 x += 2;
             } else if (arSadari[x + 1][y] == &#39;2&#39;) {
                 tempDistance += 3;
                 x += 2;
             } else if (arSadari[x + 1][y] == &#39;3&#39;) {
                 tempDistance += 9;
                 x += 2;
             }
         }
         tempDistance += 2;
         y++;
     }
     if (y &gt;= len) break;
 }

 // 대각선 보정
 tempDistance = tempDistance.replaceAll(&#39;12&#39;, &#39;1&#39;);
 tempDistance = tempDistance.replaceAll(&#39;32&#39;, &#39;3&#39;);
 tempDistance = tempDistance.replaceAll(&#39;7&#39;, &#39;27&#39;);
 tempDistance = tempDistance.replaceAll(&#39;9&#39;, &#39;29&#39;);
 for (let i = 0; i &lt; tempDistance.length; i++) {
     arDistance[i] = Number.parseInt(tempDistance.charAt(i));
 }

 return arDistance;
}</code></pre>
</li>
<li><p>경로에 따라 움직이는 함수들을 작동한다.</p>
<pre><code class="language-js"></code></pre>
</li>
</ol>
<p>function doMove(lineNum) {
    let arDistance = findSadari(sadari, lineNum);
    moveDown(lineNum);
    for (let i = 0; i &lt; arDistance.length; i++) {
        switch (arDistance[i]) {
            case 1 :
                moveJump(lineNum, 1);
                break;
            case 2 :
                moveDown(lineNum);
                break;
            case 3 :
                moveJump(lineNum, 3);
                break;
            case 4 :
                moveSide(lineNum, &quot;l&quot;);
                break;
            case 6 :
                moveSide(lineNum, &quot;r&quot;);
                break;
            case 7 :
                moveJump(lineNum, 7);
                break;
            case 9 :
                moveJump(lineNum, 9);
                break;
        }
    }
    $(&quot;.baseIconImg&quot;).eq(lineNum-1).removeAttr(&quot;onclick&quot;);</p>
<p>}</p>
<p>// 이동 방향
// 7   9
// 4   6
// 1 2 3
function moveDown(typeNumber) {
    let target = $(&quot;.baseIconImg&quot;).eq(typeNumber - 1);
    arIconTp[typeNumber - 1] += sizeTop;
    target.delay(200).animate({top: arIconTp[typeNumber - 1]}, {duration: 100});
}</p>
<p>function moveSide(typeNumber, moveDistance) {
    let target = $(&quot;.baseIconImg&quot;).eq(typeNumber - 1);
    switch (moveDistance) {
        case &quot;l&quot;:
            arIconLt[typeNumber - 1] -= sizeLeft;
            break;
        case &quot;r&quot;:
            arIconLt[typeNumber - 1] += sizeLeft;
            break;
    }
    target.delay(200).animate({left: arIconLt[typeNumber - 1]}, {duration: 100});
}</p>
<p>function moveJump(typeNumber, moveDistance) {
    let target = $(&quot;.baseIconImg&quot;).eq(typeNumber - 1);</p>
<pre><code>switch (moveDistance) {
    case 1:
        arIconTp[typeNumber - 1] += sizeTop;
        arIconLt[typeNumber - 1] -= sizeLeft;
        break;
    case 3:
        arIconTp[typeNumber - 1] += sizeTop;
        arIconLt[typeNumber - 1] += sizeLeft;
        break;
    case 7:
        arIconTp[typeNumber - 1] -= sizeTop;
        arIconLt[typeNumber - 1] -= sizeLeft;
        break;
    case 9:
        arIconTp[typeNumber - 1] -= sizeTop;
        arIconLt[typeNumber - 1] += sizeLeft;
        break;
}
target.delay(200).animate({top: arIconTp[typeNumber - 1], left: arIconLt[typeNumber - 1]}, {duration: 100});</code></pre><p>}</p>
<p>```</p>
<p>지금까지 개발한 것 중 가장 생각해내기 어려웠고 그만큼 뿌듯했던 기능인 것 같다. 성공하고 사다리를 따라 내려가는 아이콘들을 보며 얼마나 짜릿하고 희열감이 느껴졌는지 아직까지 생생하다.
<img src="https://velog.velcdn.com/images/noly-poly/post/e8dff238-73a2-44a9-bf28-0f3fa34d1a26/image.gif" alt=""></p>
</br>
</br>
</br>

<h1 id="마치며">마치며</h1>
<p>Github을 위해 시작한 팀 프로젝트였지만 소통이 아닌 스타일에 대한 갈등을 통해 협업에 대한 경험을 더 쌓고 자체 알고리즘을 구현하며 전제, 프로세스, 사용자 경험에 대한 고민을 통해 더 의미있는 경험을 할 수 있어서 좋았다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Blank, 자료 수집 챗봇, 2020]]></title>
            <link>https://velog.io/@noly-poly/blank</link>
            <guid>https://velog.io/@noly-poly/blank</guid>
            <pubDate>Thu, 17 Nov 2022 04:51:27 GMT</pubDate>
            <description><![CDATA[<h1 id="개요">개요</h1>
<h2 id="서비스">서비스</h2>
<h3 id="미리보기">미리보기</h3>
<p><img src="https://velog.velcdn.com/images/noly-poly/post/27235b6a-a9c5-49cb-97dc-42e7a4db016f/image.png" alt=""></p>
<ul>
<li><a href="http://blank.egoist.im/">Demo</a>
일상 대화는 작동하지 않음. 현재 심심이 API는 비용 문제로 유로 플랜을 중지.</li>
<li><a href="https://github.com/JISU-YANG/2020_Blank">Github</a></li>
</ul>
<hr>
<h3 id="소개">소개</h3>
<ul>
<li>서비스 명: <strong>Blank</strong></li>
<li>서비스 주제: <strong>자료 수집 챗봇</strong></li>
<li>서비스 플랫폼: <strong>데스크탑 웹 서비스</strong></li>
</ul>
<hr>
<h3 id="핵심-기능">핵심 기능</h3>
<ol>
<li>사용자가 질문을 하면 그에 대한 정보 수집 결과를 메시지로 받을 수 있다.</li>
<li>사용자는 Blank에 관한 질문했을때 소개 메시지를 받을 수 있다.</li>
<li>사용자가 일상적인 대화에 대답 메시지를 받을 수 있다.</li>
</ol>
</br>

<h2 id="프로젝트">프로젝트</h2>
<h3 id="규모">규모</h3>
<p><strong>기간</strong>, 3일</p>
<ul>
<li>20.07.27 ~ 20.07.29 </li>
</ul>
<p><strong>인원</strong>, 1명</p>
<ul>
<li>🤚
기획, 개발, UX/UI</li>
</ul>
<hr>
<h3 id="기술-스택">기술 스택</h3>
<p><strong>언어</strong></p>
<ul>
<li>Java</li>
<li>JSP/Servlet</li>
<li>JavaScript</li>
<li>HTML/CSS</li>
</ul>
<p><strong>기술</strong></p>
<ul>
<li>Spring Framework</li>
<li>Maven</li>
</ul>
<p><strong>API</strong></p>
<ul>
<li>네이버 뉴스 API</li>
<li>심심이 API</li>
</ul>
<p><strong>라이브러리</strong></p>
<ul>
<li>JSON-SIMPLE</li>
<li>OkHttp</li>
<li>Jsoup</li>
</ul>
</br>
</br>
</br>

<h1 id="기획">기획</h1>
<h2 id="1-영화가-너무-멋있는걸">1. 영화가 너무 멋있는걸</h2>
<h3 id="1-1-왜-시작하게-되었나">1-1. 왜 시작하게 되었나?</h3>
<p>많은 이들이 그랬을 것이다. 나는 아이언맨을 보고 항상 자비스(Jarvis)를 향한 동경과 갈증이 있었다. 비슷하게라도 업무를 도와줄 수 있는 BOT을 제작할 수는 없을까?라는 질문을 계속 가슴 속에서 지울 수 없었고 몇 번을 포기하려했지만 계속 눈 앞에서 아른거림에 지고 말았다.</p>
<hr>
<h2 id="2-현실적으로-바라보기">2. 현실적으로 바라보기</h2>
<h3 id="2-1-가능한가">2-1. 가능한가?</h3>
<p>일단 최대한 이론적으로 가능한 스케일을 생각했다. 하지만 과한 욕심이 담겨있었다. 간단하게 흐름도를 작성해 보았다.
<img src="https://velog.velcdn.com/images/noly-poly/post/0f3a8ad5-c4ce-425e-a324-64bafe4d8d08/image.png" alt=""></p>
<blockquote>
</blockquote>
<ol>
<li>사용자가 질문을 하고</li>
<li>어떠한 질문인지 판단하여 그에 맞는 대답을 하고</li>
<li>비슷한 유형의 질문을 찾아 연관시키고 만약 존재하지 않는다면</li>
<li>대답을 학습하는 것이다.</li>
</ol>
<p>추가로 원하는 기능을 호출하면 위젯형식으로 답하는 것도 더했다. (예를 들면 날씨와 계산 등) 기본적인 대화에서부터 사실 머신러닝과 자연어 처리을 피해갈 수가 없는 분야여서 완성된 그림을 아무리 떠올려보아도 실망스럽고 도저히 힘이 나지 않았다. 그래서 완벽하게 처음부터 다시 생각해보았다.</p>
<h3 id="2-2-현실적으로-가능한-스케일">2-2. 현실적으로 가능한 스케일</h3>
<blockquote>
</blockquote>
<ol>
<li>내가 구현가능해야 한다.</li>
<li>최소한의 시간으로 퀄리티를 내야한다.</li>
</ol>
<p>이 두가지 전제를 지키면서 내가 원하는 봇을 만들어야하는데 주제의 스케일을 좁힐 필요가 있었다. 그래서 위젯형식의 기능적이 요구되는 답을 제공하는 걸 배제했다. 그리고 2번 전제를 지키기 위해 기간상 불가능한 부분은 API(+RESTful)로 대체하기로 했다. 그리고 API를 사용함으로 DB를 사용하지 않기로 했다. 그다지 불필요한 회원기능도 뺐다. 3가지 기능으로 주제를 줄일 수 있었다.</p>
<blockquote>
</blockquote>
<ol>
<li>자신에 대해 물어봤을때 소개하는 것</li>
<li>정보수집을 요구할때 대답할 수 있는 것</li>
<li>일상적인 대화(감성적인 대화)에도 대답할 수 있는 것</li>
</ol>
<p>질문의 유형을 판단하는 로직은 API와 라이브러리를 사용하기로 하고 작업을 시작했다.</p>
<blockquote>
</blockquote>
<ul>
<li>일상대화가 가능한 API</li>
<li>정보수집을 위한 크롤링</li>
</ul>
</br>
</br>
</br>

<h1 id="개발">개발</h1>
<h2 id="1-우리는-친구가-필요해">1. 우리는 친구가 필요해</h2>
<h3 id="1-1-심심이-api">1-1. 심심이 API</h3>
<p>일상대화가 가능한 API를 찾아보던 중 <a href="https://workshop.simsimi.com/document">심심이가 API로</a> 제공되는 것을 발견했다.
<img src="https://velog.velcdn.com/images/noly-poly/post/91159210-daa5-4e9b-8bf0-e50db8c49384/image.png" alt="">
하하.. 어떻게 보내야 하는거지? 라는 생각이 들었다. 지금까지는 form 데이터, Ajax 비동기 데이터 통신 밖에 해보지 않아서 낯설었다. 하지만 예전에 잠깐 공부한 RESTful이라는 것을 알 수 있었고 java를 통해서도 사용할 수 있도록 라이브러리가 존재했다.</p>
<hr>
<h3 id="1-2-okhttp-라이브러리">1-2. OkHttp 라이브러리</h3>
<p>그 중 OkHttp라는 라이브러리를 사용했다. 가이드는 적합한게 생각보다 얼마 없었지만 사용하기 수월해보여 결정했다. 심심이 API는 데모 프로젝트를 위해 요청 100회를 무료로 지원해준다. &quot;100번 안에 완성하면 성공이다.&quot; 라는 작은 미션을 두고 라이브러리의 여러 예시들을 참고하여 구조를 파악하기 시작했다.
<img src="https://velog.velcdn.com/images/noly-poly/post/f2f11a59-0000-4d89-8b96-5436826b575f/image.png" alt=""></p>
<p>그로부터 3시간쯤 걸렸을까 성공해냈다. 정말 오랜만에 가슴이 뛰기 시작했다. 이어지는 성취감과 멋진걸 만들 수 있을거란 예감에 잠도 오지 않았다.</p>
<hr>
<h3 id="1-3-json-simple-라이브러리">1-3. JSON-SIMPLE 라이브러리</h3>
<p>받아온 JSON 데이터에서 무식하게 문자열 자르기를 시전했다가 후에 다른 파트에서도 JSON 데이터로 받아야 해서 구글 코드에서 제공하는 JSON-SIMPLE이라는 라이브러리를 사용해 JSON 객체로 파싱한 후 추출했다.</p>
</br>

<h2 id="2-유용한-정보를-찾아줘">2. 유용한 정보를 찾아줘</h2>
<p>정보수집을 위해 크롤링할 수 있는 경로를 생각해보았다.</p>
<blockquote>
</blockquote>
<ol>
<li>뉴스</li>
<li>사전</li>
<li>위키</li>
</ol>
<h3 id="2-1-네이버-뉴스">2-1. 네이버 뉴스</h3>
<p>첫번째로 네이버에 검색했을때 가장 먼저 나오는 뉴스를 가져오고 싶었다. 그런데 구글링을 통해 알아본 정보(몇년 전 포스팅된 글들)를 참고하여 시도를 해보았지만 안되길래 다시 조사를 해보았다.</p>
<p><img src="https://velog.velcdn.com/images/noly-poly/post/b89c2027-390f-4bbd-9077-6e0516d4d88d/image.png" alt=""></p>
<p>아...? 자체적으로 REST API를 제공한다. 이용 신청해서 TOKEN을 발급받은 뒤 심심이 API 때 학습한 방법으로 OkHTTP 라이브러리를 이용해 시도해보았지만 GET방식과 POST방식의 차이가 조금 있어서 조금 해맸다. 하지만 금방 해결할 수 있었다.</p>
<p><img src="https://velog.velcdn.com/images/noly-poly/post/db9b1e25-09c5-498b-a3ea-90792efd45e6/image.png" alt=""></p>
<p>JSON 타입으로 받을 수 있어서 같은 JSON-SIMPLE 라이브러리를 사용하여 추출했다. (여기서 라이브러리를 사용하기로 결심했다.)</p>
<hr>
<h3 id="2-2-위키백과">2-2. 위키백과</h3>
<p>두번째는 나무위키를 크롤링하고 싶었지만 원하는 기본적인 정의에 대한 요약을 정제하는게 쉽지 않았다. 태그가 구조화가 잘 되어있지 않았다. HTML의 기본은 well-formed하고 valid 해야한다 했건만. 다양한 사용자 기반으로 만들어지는 서비스다 보니 이해는 된다.</p>
<p>여러시도 끝에 타겟을 변경하기로 했다. 위키백과는 다행히 적합했다. 그리고 크롤링은 훈련 때 간단하게 하고 넘어가서 가물가물했지만 쉬는시간에 혼자 게임 랭킹을 이용해 닉네임으로 썸네일과 기타 정보를 가져오는 걸 몰래(!?) 해본 덕에 금방 기억이 났고 DOM(Document Object Model)과 셀렉터 표현식에 대한 이해는 그때보다 충분했기에 생각보다 엄청 간단했다. Jsoup이라는 라이브러리를 사용하여 파싱했다.</p>
<p><img src="https://velog.velcdn.com/images/noly-poly/post/53028f26-21e2-481f-9efe-7d4e6d08d616/image.png" alt=""></p>
<p>위키 특유의 참조를 안내하는 text[1]의 [1]를 제거하기 위해 정해진 몇줄을 가져온 뒤 제거해주는 과정을 추가해줬다.</p>
<hr>
<h3 id="2-3-네이버-통합사전">2-3. 네이버 통합사전</h3>
<p>위키와 비슷했지만 추가로 생각해주어야 하는 것 들이 있었다.</p>
<ol>
<li>단어와 일치하고 있지 않으면 포함하는 단어를 반환한다.</li>
<li>&quot;play&quot;,&quot;중요&quot;와 같은 제공하는 기능에 관련된 텍스트도 같이 반환된다.</li>
<li>공백이 불규칙하게 있다.</li>
</ol>
<p>이 3가지 추가적인 과정을 보완하는 로직을 추가하여 정보를 가져오는 것은 모두 성공했다.</p>
</br>

<h2 id="3-사람이-말하는-방식">3. 사람이 말하는 방식</h2>
<h3 id="3-1-내-말-알아들어">3-1. 내 말 알아들어?</h3>
<p>하나의 프로세스 메소드에서 특정 키워드를 아규먼트로 넣어주면 작동하게끔 설계했다. 그때서야 아차싶은 것이 있었다. 챗봇이다 보니 단어만 툭 하고 던지는 것이 아니지 않는가. 그래서 자연어 처리를 피할려고 했지 않은가. 어쩔 수 없이 임시방책으로 어떻게 대화를 자주 던지는지 패턴을 분석했다.</p>
<blockquote>
</blockquote>
<p>ex)
&quot;민들레가 뭐야?&quot;, &quot;해리포터는 뭐니?&quot;, &quot;개발이 뭐니?&quot;, &quot;아이언맨이 뭐니?&quot;, &quot;코로나 알아?&quot;</p>
<p>이 질문들의 공통점은 은, 는, 이, 가 + 공백(white space) 앞에 목적어의 명사가 위치한다.</p>
<p>당연하게도 유연하게 대응은 할 수 없겠지만 대부분의 경우는 처리가 가능하기 때문에 유효한 검색어라고 판단하여 그 부분을 추출하게 하였다.</p>
<hr>
<h3 id="3-2-내-말-이해해">3-2. 내 말 이해해?</h3>
<p>드디어 판단하는 클래스를 생성했다. 처음에는 자바스크립트로 이 부분을 처리를 하려고 했다. (나름 서버의 부담을 줄여보려고?) 하지만 크게 무리가 되지 않을 뿐더러 소스가 노출되면 랜덤으로 뿌려주는 인삿말의 데이터가 공개되기 때문에 미리 재미를 반감시킬 것 같아 서버단계로 옮겼다. 아 이번 프로젝트트 DB를 사용하지 않는다. 그리고 서버에 문장을 던졌을때 대답을 응답해주는 깔끔한 구조도 찬성표를 더한 것 같다. </p>
<p>문장이 서버에 넘어오면 그 문장을 분석한다.</p>
<blockquote>
</blockquote>
<ol>
<li>&quot;너는&quot;,&quot;너를&quot;,&quot;넌&quot;,&quot;너&quot;,&quot;blank&quot;,&quot;블랭크&quot;와 같은 키워드가 포함되면 본인을 소개한다.</li>
<li>&quot;알아&quot;,&quot;알려&quot;,&quot;누구&quot;,&quot;뭐&quot;,&quot;무엇&quot;,&quot;어디&quot;,&quot;언제&quot;,&quot;어느&quot;와 같은 키워드가 포함되면 정보를 반환한다.</li>
</ol>
<p>그리고 위의 두가지가 아니면 일상대화로 간주한다. 이 과정을 통해 각자의 로직을 거쳐 대답을 화면으로 응답한다.</p>
<hr>
<h3 id="3-3-데이터를-전달하는-방식">3-3. 데이터를 전달하는 방식</h3>
<p>그리고 재밌는 점은 소개, 대화는 한 줄의 문자열이고, 정보는 문자열 배열이다. 평소같았으면 DTO, LIST, MAP과 같은 객체를 사용하여 화면에 응답하게 했을지도 모른다. Ajax를 통해 MAP 객체를 사용하긴 했지만 뉴스 10줄, 사전 1줄, 위키 1줄이 생각보다 길지 않아 구분자와 합쳐주어 화면에서 스크립트로 split해서 뿌려주었다.
<del>(혼자하는 프로젝트니 해보고 싶었는데 멍청한 방법이면 알려주길 바람.)</del></p>
</br>
</br>
</br>


<h1 id="uiux">UI/UX</h1>
<h2 id="1-옷이-사람을-만든다">1. 옷이 사람(?)을 만든다</h2>
<h3 id="1-1-전형적인-챗봇에서-벗어나기">1-1. 전형적인 챗봇에서 벗어나기</h3>
<p>Ajax로 화면으로 넘기는 작업을 하던 중 UX/UI를 고민하게 되었다. 전형적인 챗봇 이미지는 싫고 오로지 1:1 대화이다 보니 색다른 느낌을 주고 싶었다.</p>
<p><img src="https://velog.velcdn.com/images/noly-poly/post/1a8d8a86-1e3d-4ca8-9adf-999b3b380e5e/image.png" alt=""></p>
<p>그래서 고민 끝에 나온 1차 디자인이다. 다크 테마에 그라디언트 애니메이션이 적용된 로고, 대답은 fade in/out 애니메이션이 적용되었다. jQuery와 CSS를 이용했다.</p>
<hr>
<h3 id="1-2-전형적인-챗봇으로-돌아오기">1-2. 전형적인 챗봇으로 돌아오기</h3>
<p>문제가 생겼다. 전형적인 챗봇의 UX/UI는 싫은데 한 줄의 대답과 여러 줄의 대답을 전달하는 것이 전혀 자연스럽지가 않았다. 그리고 전체화면으로도 사용할 수 있지만 주로 작게 탭을 띄어서 사용할 예정이라 깨지지 않아야했다. 그래서 다른 방향으로 아이디어를 내려고 노력했다. 이 과정만 3시간은 넘게 걸렸던거 같다. 너무 흔해빠지고 전형적이지 않은 디자인을 css로 구현하는 것도 4시간정도 걸렸다.</p>
<p>컨셉은 바로 1:1 대화가 아니라 팀 채팅이라고 느껴지게 한 후 각자의 맡은 역할을 가진 팀원들이 대답을 해주는 컨셉이였다. 그리고 너무 진지하게 느껴지지 않기 위해 MARVLE의 캐릭터의 특징을 가진 프로필 아이콘을 제작하였다. (구매한 후 흑백으로 편집)</p>
<p><img src="https://velog.velcdn.com/images/noly-poly/post/d3bbc3f1-e4af-4c77-b0a6-ebe95922683f/image.png" alt=""></p>
<p>접속한 사용자는 아이피로 닉네임이 대체되고 총 5명의 팀원으로 구성된다. 각자의 맡은 상황에 출동하여 대답을 해주며 헤더부분에 프로필 이미지 우측 상단에 불이 켜지도록 하여 강조했다.</p>
<blockquote>
</blockquote>
<ol>
<li>인사부장(소개)</li>
<li>언어교수(사전)</li>
<li>신문기자(뉴스)</li>
<li>수다쟁이(대화)</li>
<li>잡학다식(위키)</li>
</ol>
</br>

<h2 id="2-매너가-사람을-만든다">2. 매너가 사람(?)을 만든다</h2>
<h3 id="2-1-편의성을-위한-기능">2-1. 편의성을 위한 기능</h3>
<p>채팅을 지울 수 있는 새로고침(body의 태그를 지움, refresh 아님) 함수와 도움말을 hover 했을때 보여줄 수 있는 안내 tip을 안내 받을 수 있게 했는데, 또 이상한 욕심이 생겨 아무것도 없는 div태그 한개로 모든 아이콘에 우려먹는 함수를 만들었다. 사실 위, 아래는 따로 만들려다가 중간에 수식 2줄만 변경되면 돼서 이것도 합쳐버렸다. 재사용성! 그리고 좀 더 채팅의 느낌을 주기 위해 원하는 형식의 시간(타임스탬프)를 반환하는 함수도 만들었다. </p>
<hr>
<h3 id="2-2-채팅하는-느낌">2-2. 채팅하는 느낌</h3>
<p>먼저 더미 데이터를 넣어 구조를 완성한후 주고 받는 채팅의 효과를 주기위해 jQuery의 append 함수를 사용하는 자체 함수를 만들었다. 경우는 두 가지 이다.</p>
<blockquote>
</blockquote>
<ol>
<li>질문하고(내가 말하고)</li>
<li>대답을 듣는다.(Bot이 말한다)</li>
</ol>
<p>질문하는 것은 내가 고정이기때문에 크게 변화가 없다. 어떤 질문을 했는지에 대해 기록이 쌓일 수 있게 해두었다. 대답을 듣는 과정에서(챗봇이 말할 차례) 누가 어떤 이야기를 해야한다. 두 개의 파라미터로 닉네임, 프로필 이미지를 결정하게 했다. 그리고 마지막 채팅이 가장 하단에 올 수 있도록 jQuery의 scrollTop 함수를 이용하였다.</p>
</br>
</br>
</br>

<h1 id="마치며">마치며</h1>
<h3 id="기대한-것은-자비스가-아니다">기대한 것은 자비스가 아니다</h3>
<p>결과적으로 기간에 비례해서 만족을 타협하긴 했지만 실질적으로 유용성이 떨어진다. 주제가 아무래도 빅데이터, 딥 러닝같은 전문기술 없이는 매력을 갖추기가 어려운 분야였던 것 같다.</p>
<blockquote>
</blockquote>
<p>영화 &#39;아이언맨 3&#39;에서 겨우 목숨을 부지하고 아무런 지원과 자원이 없는 외지에서 좌절만 하고 있는 &#39;토니 스타크&#39;에게 꼬마 아이인 &#39;할리 키너&#39;가 이런 말을 건넨다.
&quot;정비공이라고 그랬죠? 뭐라도 만들지 그래요?&quot;</p>
<p>물론 영화 속 &#39;토니 스타크&#39;처럼 천재 엔지니어는 아니지만, 나는 개발자이고 주어진 환경과 내가 사용할 수 있는 기술 수준에서 필요한 것을 최대한 유사하게 실현하려는 경험은 꼭 필요하다고 생각한다. 그것이 더 발전된 기술을 배우고자 하는 동기가 되기도 한다. 이번 과정에서 기술적으로도 새로 알게된 것들과 함께 재밌는 경험으로 남을 것 같다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Hyper, 스마트 북마크 서비스, 2020]]></title>
            <link>https://velog.io/@noly-poly/hyper</link>
            <guid>https://velog.io/@noly-poly/hyper</guid>
            <pubDate>Thu, 10 Nov 2022 07:22:44 GMT</pubDate>
            <description><![CDATA[<h2 id="프로젝트-설명">프로젝트 설명</h2>
<h3 id="프로젝트-개요">프로젝트 개요</h3>
<ul>
<li><strong>주제</strong>
스마트 북마크 서비스
(편리하고 빠른 나만의 시작페이지)</br></li>
<li><strong>플랫폼</strong>
데스크탑 웹 서비스</br></li>
<li><strong>기간</strong>
전체 기간: 20.02.12 ~ 20.03.31
기획 기간: 20.02.12 ~ 20.03.05
개발 기간: 20.03.06 ~ 20.03.31</br></li>
<li><strong>인원</strong>: 2명<ul>
<li>🤚: <strong>PM</strong><ul>
<li>위젯(사용자 정보, 단축키, 시계, 대표 디데이)</li>
<li>모드(빠른검색, 즐겨찾기, 편집모드)</li>
<li>회원(회원가입), 바로접속</li>
<li>환경설정(테마, 배경화면)</li>
<li>웹 로그, 시스템 단축키</li>
</ul>
</li>
<li>👩‍💻:<ul>
<li>위젯(디데이, 메모)</li>
<li>중복검사(닉네임, 이메일), 비밀번호 암호화</li>
<li>로그인, 닉네임 변경, 비밀번호 재설정, 회원탈퇴</li>
<li>이미지 업로드</li>
</ul>
</li>
</ul>
</li>
</ul>
</br>

<h3 id="프로젝트-핵심-기능">프로젝트 핵심 기능</h3>
<blockquote>
</blockquote>
<p>자주 이용하는 웹 사이트나 서비스를 접근하는 방법이 불편하다는 생각이 들었다. 그래서 최대한 단순하고 시간이 적게 걸리는 방법을 고민하였고, 미리 일정량의 정보를 제공하고 키보드로 컨트롤 하는 것을 고안했다.
</br>
나를 위한 서비스로 프로토 타입을 개발하였으나, 주변의 반응과 요청으로 회원 기능을 추가하고 부가 기능을 보태어 Hyper 프로젝트를 완성하게 되었다.</p>
<ul>
<li>책상 위에 있는 물건들을 위젯화한다. (시계, 캘린더, 메모지, 액자, 명함)</li>
<li>자신의 즐겨찾기를 단축키로 접근할 수 있다.</li>
<li>검색엔진을 바로 이용할 수 있다. (구글, 네이버, 유튜브)</li>
</ul>
</br>

<h3 id="프로젝트-기술-스택">프로젝트 기술 스택</h3>
<ul>
<li>언어<ul>
<li>Java</li>
<li>JSP</li>
<li>JavaScript</li>
<li>HTML/CSS</li>
</ul>
</li>
<li>기술<ul>
<li>Spring Framework</li>
<li>MyBatis</li>
<li>Json</li>
</ul>
</li>
<li>라이브러리<ul>
<li>jQuery</li>
<li>commons fileupload</li>
<li>commons-io</li>
<li>Spring-Security </li>
</ul>
</li>
<li>API<ul>
<li>HTML5 File API</li>
</ul>
</li>
</ul>
</br>

<h3 id="프로젝트-erd">프로젝트 ERD</h3>
<p><img src="https://velog.velcdn.com/images/noly-poly/post/7749d463-4d1c-4312-87cf-aa7bd1a34e75/image.png" alt=""></p>
</br>

<h3 id="프로젝트-미리보기">프로젝트 미리보기</h3>
<p><img src="https://velog.velcdn.com/images/noly-poly/post/0434f2b1-1e82-4441-a9ac-fb4afd8e35fd/image.png" alt=""></p>
<ul>
<li><a href="https://hyper.egoist.im/">Demo</a></li>
<li><a href="https://github.com/JISU-YANG/2020_Hyper">Github</a></li>
</ul>
</br>
</br>
</br>

<h2 id="keep">Keep</h2>
<h3 id="1-즐겨찾기-아이콘">1. 즐겨찾기 아이콘</h3>
<p><img src="https://velog.velcdn.com/images/noly-poly/post/ab0cc4f3-e871-4144-96eb-a36dae40677e/image.png" alt=""></p>
<p>처음 기획할 때는 해당 사이트의 favicon을 크롤링해 자동으로 등록되는 기능으로 구현하려고 했다. 하지만 컬러나 형태, 배경 유무 등 일체감이 너무 떨어져서 자체 아이콘 설정 기능으로 새로 개발하기로 했다. Font Awesome 5 API를 CDN으로 이용했고 classs name으로 치환되는 방식으로 컨트롤했다. 편집 모드에서 키를 선택하게 되면 Modal이 뜨는데 위의 스크린샷과 같이 즐겨찾기 링크의 설정과 동시에 할 수 있도록 했다. 원하는 아이콘을 선택하면 hidden type Input에 해당 아이콘의 value가 저장되어 사용자가 임시 선택을 하고 체크 버튼으로 반영하게 해서 코드는 굉장히 짧지만 자연스러운 흐름으로 반영될 수 있어 만족스러웠다.</p>
<pre><code class="language-html">&lt;div class=&quot;hotEdit&quot; data-key=&quot;&lt;%=arKey[i - 1][j]%&gt;&quot;&gt;
    &lt;!-- 링크 --&gt;
    &lt;div class=&quot;editLink&quot;&gt;
        &lt;input type=&quot;text&quot; class=&quot;editLinkInput&quot; data-key=&quot;&lt;%=arKey[i - 1][j]%&gt;&quot;
            placeholder=&quot;&lt;%=arKey[i - 1][j]%&gt;:url ex) www.naver.com/&quot; /&gt;
    &lt;/div&gt;

    &lt;!-- 아이콘 --&gt;
    &lt;div class=&quot;editIcon&quot;&gt;
        &lt;i onclick=&quot;editChange(&#39;&#39;);&quot;&gt; &lt;/i&gt;
        &lt;% for (int k = 0; k &lt; arIconSet.length; k++) { %&gt;
        &lt;i class=&quot;&lt;%=arIconSet[k]%&gt;&quot; data-click=&quot;false&quot;
            onclick=&quot;editChange(&#39;&lt;%=arIconSet[k]%&gt;&#39;);&quot;&gt;&lt;/i&gt;
        &lt;% } %&gt;
    &lt;/div&gt;

    &lt;!-- 적용 --&gt;
    &lt;div class=&quot;editBtn&quot;&gt;
        &lt;i class=&quot;fas fa-check&quot; onclick=&quot;editSave(&#39;&lt;%=arKey[i - 1][j]%&gt;&#39;);&quot;&gt;&lt;/i&gt;
    &lt;/div&gt;

&lt;/div&gt;</code></pre>
<hr>
<h3 id="2-단축키-제어">2. 단축키 제어</h3>
<p><img src="https://velog.velcdn.com/images/noly-poly/post/e86882b1-c9b2-4dcd-b144-3b3fcd86a740/image.png" alt=""></p>
<p>단축키가 서비스 전반에 다양한 곳에 적용되어 있다. 그래서 아래와 같이 구분을 했다.</p>
<ul>
<li>Global (모드 변경을 위한 키)</li>
<li>Local (모드의 기능을 사용하기 위한 키)</li>
<li>예외 (input 태그에 입력중일 때 등)</li>
</ul>
<p>중복으로 인식하는 일을 막기 위해 모드에 맞게 키 연결이 대체되거나 일시적으로 작동이 멈추는 상황이 필요했다. 각 범위에 논리타입의 변수와 조건문으로 Handling하는 로직을 추가했다. 서비스에서 사용되지는 않지만 모든 키 입력에 대응하던 부분을 ASCII 코드상의 범위를 알파벳 범위로 제한하고 각 키에 대응하는 입력값 연결로 구현했다.</p>
<pre><code class="language-javascript">/*
 * 0 : off, 1: on
 */
function activeChange(choice) {
    if(choice){
        active = false;
    }else{
        active = true;
    }
}</code></pre>
<pre><code class="language-javascript">function modeChange() {
    if (active) {
        if (mode == &quot;link&quot; &amp;&amp; event.keyCode == 39) {
            keydownTrans();
        }else if (mode == &quot;search&quot; &amp;&amp; event.keyCode == 37){
            keydownTrans();
        }else if (event.keyCode == 9) {
            keydownTab();
        }else if (event.keyCode&gt;=65 &amp;&amp; event.keyCode&lt;=90 &amp;&amp; mode==&quot;link&quot;){
            keydownLink();
        }else if(mode == &quot;search&quot; &amp;&amp; event.keyCode == 13){
            keydownEnter();
        }
    }
}</code></pre>
<p>즐겨찾기 모드는 데이터 관리 편의를 위해 배열단위로 값들을 서버에서 응답 받는데 처음에는 리스트로 제공을 해서 문제가 없었지만, 단축키라는 특징을 직관적으로 사용자에게 전달하는 경험을 추가하기 위해 화면에서 현실의 키보드 배치로 제공하기로 변경되었다. 이 부분의 코드와 과정을 줄이기 위해 줄 간 키 개수의 패턴이 10, 9, 7로 (n-i)같이 떨어지는 것을 이용했고, 처리과정에서 객체의 개수를 줄여 코드를 많이 줄일 수 있었다. </p>
<hr>
<h3 id="3-웹-로그">3. 웹 로그</h3>
<p><img src="https://velog.velcdn.com/images/noly-poly/post/73589dd2-0928-4c51-a1da-39e15ba3184e/image.png" alt=""></p>
<p>이 서비스는 TOMCAT 서버 호스팅을 이용했는데 Server의 Console과 Log에 접근하기 위해서는 ftp를 이용해야만 했다. 서버를 중단해야했던 적도 있었다. 그래서 모바일로도 접근이 가능한 웹로그를 개발했다. 웹 로그 컨트롤러를 따로 두고 일부 Joinpoint에서 메소드 호출했고, 전체검색/날짜별검색/상세검색(날짜, 태그별)으로 3가지 쿼리를 이용해 화면의 유연한 요청을 처리하게끔 구현했다.</p>
</br>

<h2 id="problem--try">Problem &amp; Try</h2>
<h3 id="1-개발-표준을-정하자">1. 개발 표준을 정하자</h3>
<p>프로젝트를 시작할때 명명법이나 구조적인 부분에 대해 회의와 규칙을 정하지 못 했다. 그래서 잔 에러나 소통 실수가 생겨났다. 그리고 중반부에 알게된 것이 컨트롤러에 기능적인 로직을 최대한 줄이고 서비스 단계에서 마무리를 하는게 더 좋았을 것이라는 생각이 들었다. 그 부분이 찝찝한 아쉬움으로 남는다. </p>
<hr>
<h3 id="2-익숙한-것-벗어나기">2. 익숙한 것 벗어나기</h3>
<p>사용자 편의성을 고려하는데 분석을 많이 했고 그것을 반영한 설계로 기능들의 로직흐름이 변화했다. 그리고 나에게 익숙했던 Server 단계가 아닌 기술 트렌드에 맞춰 스크립트로 기능들을 최대한 구현하려고 신경썼다. 그 결과, 빠른 처리 속도와 서버의 부하를 줄일 수 있어서 적은 비용으로 더 많은 사용자들을 수용할 수 있게 되었다.</p>
<hr>
<h3 id="3-기간을-위해-보류">3. 기간을 위해 보류</h3>
<p>중간중간 좋은 아이디어들이 많이 나왔다. 중요도가 높거나 시간이 별로 소모되지 않는 경우 반영되기도 했지만 보통 보류된 것들이 많다. Social기능을 강화한 하이퍼 공유, 즐겨찾는 하이퍼, 추천 url 기능과 비즈니스 목적으로도 사용가능한 Hyper 플러스, iframe을 이용한 위젯형태의 모바일 사이트 연동, 플러그인 형태의 사용자 위젯 마켓 등 욕심이 나는 것들이 참 많았다. 만약 2.0 프로젝트를 기획하게 된다면 꼭 해보고 싶다.</p>
</br>
</br>
</br>

<h2 id="마치며">마치며</h2>
<p>결과적으로 담당 기능도 잘 나누어졌고 나름 예측 범위 안에서 흘러가서 프로젝트 기간이 길어지지 않았고, 퀄리티도 괜찮은 수준으로 개발되었다. 아무래도 요새 PC보다는 스마트폰을 사용하다보니 일반인 지인들에게는 피드백을 받지 못 했지만, 개발자 지인들의 반응이 뜨거웠고 과정, 결과 모두 뿌듯할 수 있었다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[NAWA, 모임 중개 서비스, 2019]]></title>
            <link>https://velog.io/@noly-poly/nawa</link>
            <guid>https://velog.io/@noly-poly/nawa</guid>
            <pubDate>Fri, 04 Nov 2022 12:30:10 GMT</pubDate>
            <description><![CDATA[<h2 id="프로젝트-개요">프로젝트 개요</h2>
<blockquote>
</blockquote>
<ul>
<li><strong>주제</strong>:
모임 중개 서비스</br></li>
<li><strong>플랫폼</strong>:
데스크탑 웹 서비스</br></li>
<li><strong>기간</strong>:
전체 기간: 19.11.12 ~ 20.02.12 (중단)
기획 및 서류 작성: 19.11.12 ~ 19.12.12
1차 개발 기간: 19.12.13 ~ 20.01.12
2차 개발 기간: 20.01.13 ~ 20.02.12</br></li>
<li><strong>인원</strong>: 6명<ul>
<li>🤚: <strong>PM</strong></li>
<li>👩‍💻: <strong>PE</strong>, 소셜 로그인 API, 지도 API</li>
<li>👨‍💻: 이벤트 기본, 기타, 상태</li>
<li>👨‍💻: 회원 가입, 수정, 관리</li>
<li>👩‍💻: 이벤트 참가, 공지사항 관리</li>
<li>👩‍💻: 이벤트 문의, 후기</li>
</ul>
</li>
</ul>
<h2 id="프로젝트-핵심-기능">프로젝트 핵심 기능</h2>
<ul>
<li>소셜 로그인, 2단계 회원가입(선택정보)</li>
<li>관리자 회원 관리</li>
<li>공지사항 관리</li>
<li>이벤트 대시보드</li>
<li>이벤트 관리, 재개설, 조기 마감, 추가 모집</li>
<li>이벤트 문의, 후기</li>
</ul>
<h2 id="프로젝트-기술-스택">프로젝트 기술 스택</h2>
<ul>
<li><strong>언어</strong><ul>
<li>Java</li>
<li>JavaScript</li>
<li>HTML/CSS</li>
<li>XML</li>
<li>SQL</li>
<li>JSP/Servlet</li>
</ul>
</li>
<li><strong>기술</strong><ul>
<li>MyBatis</li>
<li>AJAX</li>
<li>JSON</li>
<li>Spring Framework</li>
</ul>
</li>
<li><strong>API</strong><ul>
<li>Google Login</li>
<li>kakao Login</li>
<li>Kakao Maps</li>
</ul>
</li>
<li><strong>라이브러리</strong><ul>
<li>Jackson</li>
<li>commons-fileupload, commons-io</li>
<li>javax.mail</li>
<li>Spring Social, Spring Scheduler, Spring Security, Spring-AOP</li>
<li>Gson</li>
<li>JSTL</li>
<li>jQuery</li>
<li>Bootstrap</li>
</ul>
</li>
</ul>
<h2 id="프로젝트-미리보기">프로젝트 미리보기</h2>
<h3 id="서비스-스크린샷">서비스 스크린샷</h3>
<p><img src="https://velog.velcdn.com/images/noly-poly/post/d232f978-eb9e-48ca-aa76-8bda7f5f77fb/image.png" alt=""></p>
<p><a href="https://github.com/JISU-YANG/2019_NAWA">Github</a></p>
<h3 id="서비스-아키텍처">서비스 아키텍처</h3>
<p><img src="https://velog.velcdn.com/images/noly-poly/post/83c76f9e-6b8a-4849-a553-ae84d86c406c/image.png" alt=""></p>
<h3 id="서비스-erd">서비스 ERD</h3>
<p><img src="https://velog.velcdn.com/images/noly-poly/post/08a07d98-3e48-4cec-9533-683e2e5acea0/image.PNG" alt=""></p>
<h3 id="프로젝트-문서">프로젝트 문서</h3>
<p><img src="https://velog.velcdn.com/images/noly-poly/post/f55b8548-10eb-42b5-aef5-9319add2b33e/image.png" alt=""></p>
</br>
</br>
</br>

<blockquote>
</blockquote>
<p>원래 Keep, Problem, Try를 돌아보던 방식과 달리 이번 프로젝트 회고는 복기하며 어떻게하면 상황이 달랐을 수 있을지에 대한 고민을 적으려고 한다.</p>
<h2 id="욕심을-선택하다">욕심을 선택하다.</h2>
<h3 id="1-팀">1. 팀</h3>
<p>과정에 참여한 것은 취업을 위해서이고 이 프로젝트의 목적도 취업을 위한 포트폴리오였다. 이기적으로 생각하고 욕심을 가졌다면 혼자 프로젝트를 진행해도 이상하지 않았을 것이다. 하지만 나는 팀으로서 같이 성장하고 싶었고, 같이 과정동안 고생한 팀원들과 함께 잘 되었으면 했다. 그래서 선택을 해야했다. 목적이 명확하니 쉽게 결정할 수 있었팀과 프로젝트 중 팀을 선택했다. 다들 어려워하며 몰라서 불안해하는 마음을 해결책과 함께 다독이며 더 주도적으로 열심히 움직였다.</p>
<h3 id="2-프로젝트">2. 프로젝트</h3>
<p>학원에서 두 달, 따로 만나며 한 달, 각자 추가로 한 달 이렇게 총 4달 동안 진행된 프로젝트이다. 프로젝트 기획(요구사항) 및 설계 등 기반이 되는 문서 작업에만 한달을 몰두해 신경을 많이 썼다. 그만큼 기능을 구현하기 시작해서는 변동사항도 크게 없었다. Git을 사용할 줄 모르다보니 테스트가 완료된 기능을 합쳐가며 진행했다. 훈련이 종료가 된 이후에도 카페, 소호 사무실을 전전하며 다들 고생이 많았다. 하지만 결과적으로 프로젝트는 미완성으로 중단하기로 결정됐다. 이유는 데드라인을 지키지 못 하고 여러번 프로젝트 기간이 연장 되었기 때문이었다. 다들 취업이 급했는데 완성될 기미가 쉽게 보이지 않았다. 돌아보면 결국 내가 PM이었기 때문에, 내 책임이다. 욕심도 많았고 모두 최선을 다 해주었기 때문에 너무 아쉽고 속상하다.</p>
</br>

<h2 id="다른-결과일-수-있었을까">다른 결과일 수 있었을까?</h2>
<h3 id="1-도움의-범위">1. 도움의 범위</h3>
<p>검색하거나 흔하게 찾아볼 수 있는 주제를 정말 싫어한다. 가급적이면 만드는 과정에서 결과물을 머리 속에 그리며 신이 날 수 있는 그런 주제를 찾고 싶어한다. 논의를 통해 흡사 SNS의 &#39;공유 일기장&#39; 서비스를 진행하려고 했는데 인원에 비해 스케일이 작다고 강사님께 동의를 받지 못 했다. 그래서 고민 끝에 결정된 주제가 &#39;모임 중개 서비스&#39;이다.</p>
<p>하지만 단순히 인원 수에 맞춰 스케일을 늘렸다 보니 팀원들이 자기 생각대로 쉽게 해결이 되지 않는 경우가 많았다. 알 수 없는 에러를 찾거나 실수들도 많았고 그런 디버그 과정에서도 시간을 많이 할애하게 되었다. 로직을 구성하거나 참조 자료를 찾지 못하여 곤란한 경우도 많아 내 담당 기능은 후 순위로 미루고 PM인 내가 이곳, 저곳 항상 바쁘게 뛰어다녔다.</p>
<p>그럼에도 WBS의 일정대로 진행되지 않은 경우가 다반사였고, 그게 쌓이다 보니 프로젝트 기간 자체를 기한안에 완성할 수 없었다. 연장을 하고 또 연장을 했는데도 추가적으로 연장이 필요해보였고 결국 중단하게 되었는데 촉박한 데드라인의 압박감 때문에 직접 해결해주는 경우도 많았다. 돌아보면 프로젝트의 완성, 미완성을 떠나 PM으로서 가장 후회되는 점이기도 하다. 만능 해결사같은 가이드 역할 보다는 길을 가다 가끔 만날 수 있는 안내표지판과 같은 역할을 지향했어야 그들의 성장에도 도움이 더 됐을 것이고 내 담당 기능들도 미완으로 남지 않았을 것이라는 아쉬움이 많이 든다.</p>
<hr>
<h3 id="2-프로젝트-기간의-비율">2. 프로젝트 기간의 비율</h3>
<p>훈련에서 강사님의 말씀중 &quot;현업의 절반 이상은 문서작업이다.&quot; 라는 자주 하셨다. 그래서 다양한 문서들을 제대로 경험해보는 것을 중요하게 생각했었다. 그리고 만약 과정 기간(2달) 안에 끝내지 못 하게 되더라도 도움이 될거라는 생각이었다. 하지만 지금 결과적으로 보면 정작 중요한 프로젝트의 개발을 끝내 마무리 못했다.</p>
<p>원래 개발 기간의 절반을 할애하는 동안에 기능 구현의 지체를 예상하지 못 한 전략 미스였다. 더 여유 있게 비율을 두고 퀄리티를 낮추는 선택이 필요했어 보인다. <a href="https://www.theteams.kr/teams/392/post/64327">링크된 글</a>의 필자는 소프트웨어 디자인과 기획에 30%, 실제 개발에 50%, 테스트에 20%를 투여하는 것을 프로젝트의 기준을 세우는 원칙으로 정하셨다고 한다. 또한, 테스트 코드에 관해서도 자세히 배우지 못 했고 비중있게 생각하지 못 했는데 너무 아쉽다.</p>
<hr>
<h3 id="3-미숙한-개발자-준비생">3. 미숙한 개발자 준비생</h3>
<p>내가 또 간과한 것이 다들 각자 자기 자신의 역량 잘 파악 하지 못 할 것이라는 것을 생각 못 했다. 그래서 담당 기능을 분배할 때 1차적으로 팀원 별로 잘 할 것 같은 기능을 나누고, 2차적으로 해당 팀원의 의견에 비중을 두었다. 그리고 그 담당 기능에 대해서는 변동이 없었다. 모두 수강생의 입장에서 함께 고생하며 최선을 다 했음은 변하지 않는다. 그렇다면 PM인 나의 책임인데, 만약 잦은 중간 정검을 통해 담당 기능을 과감하게 조정하는 선택을 했었더라면 더 나앗을 것이라고 생각된다. 담당 기능이 변경되는 일이 담당 팀원에게 상처가 될 것이라고 생각했었는데, 오히려 부담감과 함께 더 미안함을 가중시킨 것 같다.</p>
</br>
</br>
</br>

<h1 id="마치며">마치며</h1>
<p>고생은 참 많이하고 자랑할 수 있는 로직이나 코드가 많이 없는게 너무 아쉽게 생각한다. 또한, 가끔 속으로 팀원을 원망하기도 했지만, 결국 &quot;프로젝트의 실패는 PM의 책임이다.&quot;라는 말이 있듯이 PM으로서 나의 부족함도 많았다. 이번 프로젝트를 통해 개발자들과 협업을 위해 PM, 팀원으로서 어떻게 나아가야할지에 대한 방향이 이 회고에 적힌 것 같다. <a href="https://www.wanted.co.kr/community/post/5849">&quot;프로젝트는 실패였지만 커리어의 실패는 아니었다.&quot;</a> 라는 말이 가장 크게 새겨질 것 같다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[AirBooks, 도서 관리 프로그램, 2019]]></title>
            <link>https://velog.io/@noly-poly/airbooks</link>
            <guid>https://velog.io/@noly-poly/airbooks</guid>
            <pubDate>Thu, 03 Nov 2022 15:32:32 GMT</pubDate>
            <description><![CDATA[<h2 id="프로젝트-개요">프로젝트 개요</h2>
<blockquote>
</blockquote>
<ul>
<li><strong>주제</strong>:
도서 관리 프로그램</br></li>
<li><strong>플랫폼</strong>:
데스크탑 응용 프로그램</br></li>
<li><strong>기간</strong>:
전체 기간: 19.05.23 ~ 19.06.05, 14일</br></li>
<li><strong>인원</strong>: 6명<ul>
<li>🤚: </li>
<li><em>PM*</em>, 아키텍처 설계, 프로젝트 병합, UI/UX </li>
<li>👨‍💻: 파일 입출력</li>
<li>👩‍💻: 회원가입, 비밀번호 변경 기능</li>
<li>👩‍💻: 로그인 기능</li>
<li>👩‍💻: 대여, 반납 기능</li>
<li>👨‍💻: 검색 기능 기능</li>
</ul>
</li>
</ul>
</br>

<h2 id="프로젝트-핵심-기능">프로젝트 핵심 기능</h2>
<ol>
<li>유저가 로그인을 할 수 있다.</li>
<li>도서관의 책을 검색할 수 있다.</li>
<li>재고가 있는 도서를 대여할 수 있다.</li>
<li>현재 읽고 있는 책을 확인할 수 있다.</li>
<li>추천 책을 볼 수 있다.</li>
<li>유저, 도서 데이터가 저장이 가능하다.</li>
</ol>
</br>

<h2 id="프로젝트-기술-스택">프로젝트 기술 스택</h2>
<ul>
<li><p>Java</p>
</li>
<li><p>AWT, Swing, File I/O</p>
</li>
</ul>
</br>

<h2 id="프로젝트-미리보기">프로젝트 미리보기</h2>
<p><img src="https://velog.velcdn.com/images/noly-poly/post/ccf54f31-387f-47be-8974-4e3cc16c2133/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/noly-poly/post/390dc1c4-ba56-41b0-ac64-5cb685898925/image.jpg" alt=""></p>
<p><img src="https://velog.velcdn.com/images/noly-poly/post/90947b08-5fd0-4216-af64-0e4fa5af703f/image.jpg" alt=""></p>
<p><a href="https://github.com/JISU-YANG/2019_AirBooks">Github</a></p>
</br>
</br>
</br>

<h2 id="keep">Keep</h2>
<h3 id="1-로직이-분리된-아키텍처">1. 로직이 분리된 아키텍처</h3>
<p>모든 과정이 Java로만 개발되어 더욱 로직의 분리가 중요하다고 생각했다. 코드를 독립적이고 중복을 줄일 수 있도록 view, dao, db로 크게 나누고 기능의 분류에 맞춰 클래스를 설계 했다. </p>
<p><img src="https://velog.velcdn.com/images/noly-poly/post/8c3c78cc-953b-4bd2-9e41-a8bdaa4085c7/image.png" alt=""></p>
<hr>
<h3 id="2-pm의-영역">2. PM의 영역</h3>
<p>지금까지 내가 생각하는 PM의 큰 역할은 의지할 수 있고, 의견을 잘 낼 수 있도록 대화를 이끌어주며, 불만에 대한 대책을 반영해주는 것이라고 생각했다. 그것이 기한 안에 프로젝트를 좋은 팀 컨디션으로 최선의 퀄리티라고 생각했다. 하지만 놓친 것이 있었다.</p>
<p>이번 프로젝트를 통해 경험해볼 수 있었는데 PM에게 너무 의존적인 구조를 내가 형성하고 있었는지도 모르겠다는 생각이 들었다. 예를들어 해결하기 어려워하는 상황에서 대신 작업을 해주는 도움을 주지 않았다. 큰 틀에서 서로 지켜야하는 부분, 구현되어야하는 기능의 요소을 더 강조했었다.</p>
<p>그리고 그것에 대해 디테일하게 칭찬해주었다. &quot;이렇게 구성하면 ~ 이런 상황에서도 문제가 없겠네요.&quot; 처럼. 어떻게 보면 그 부분까지 고려하고 작성한 것이 아닐지라도 다음 작업을 할때 더 많은 상황을 고려하게 유도한 것이다.</p>
<p>그리고 맡은 기능들이 이번 프로젝트가 아니더라도 어떤 곳에서 사용될 수 있을지에 대해 이야기해주며 단기적 목표로 인한 성장에 대해 체감될 수 있도록 노력했다. 이러한 노력끝에 포기하거나 이탈자없이 기한안에 잘 마무리할 수 있었다.</p>
</br>

<h2 id="problem--try">Problem &amp; Try</h2>
<h3 id="1-db를-txt-file로-대체">1. DB를 txt file로 대체</h3>
<p>DB를 따로 사용하지 않았기 때문에 프로그램이 종료되면 인스턴스가 사라짐으로 인해 데이터가 누적이 안 되는 부분을 파일 입출력을 통한 대안을 적용했다. 하지만 현 수준에서 File I/O를 구현하는데 약간의 어려움이 있었고 시간을 많이 소모해 프로젝트 내에서 사용할 수 있도록 라이브러리 형태로 다루지는 못 했다. Path들에 대해서만 Class로 묶어두었다.</p>
<p><img src="https://velog.velcdn.com/images/noly-poly/post/fea56045-a9a0-40e0-9e93-749988deea57/image.png" alt=""></p>
<p>그런 상황에서 다른 팀원들도 이 기능을 숙지시키는 것은 무리였고, 담당 팀원과 내가 기능과 연결시키고 테스트하는 역할을 했다. 시연 전 마지막 테스트에서 데이터가 저장될 외부 경로를 설정하는데 Mac과 Windows OS간 사용자 기본 경로의 차이가 있어 System 클래스의 getProperty 메소드를 이용해 해결했다.</p>
<pre><code class="language-Java">System.getProperty(&quot;user.home&quot;)</code></pre>
<hr>
<h3 id="2-인코딩-이슈">2. 인코딩 이슈</h3>
<p>프로젝트 시작 직후 모든 팀원들의 개발환경을 일치시켰어야 했는데, 같은 강의를 듣고 있는 수강생들이라 당연하게 같을줄 알고 넘어갔었다. 기능들을 합칠때 문제가 발생했고, 그제서야 다시 확인을 하고 정정했다. 그래서 깨진 부분들은 다른 에디터를 사용하여 Copy&amp;Paste 했다. 규모가 큰 프로젝트였으면 큰일날뻔했다. 명심하자.</p>
<hr>
<h3 id="3-화면-리프래시">3. 화면 리프래시</h3>
<p>화면의 구성요소가 바뀌어도 바로 화면에 반영이 되지 않았다. 현재 켜있는 메인 프로그램을 그대로 사용하면서 수정이 되어야 했는데, JPanel 인스턴스에 removeAll() 호출한 뒤 아래와 같은 과정으로 해결했다.</p>
<p><img src="https://velog.velcdn.com/images/noly-poly/post/d03725f2-8c8b-4644-8b45-03bf0cc5ef88/image.png" alt=""></p>
</br>
</br>
</br>

<h2 id="마치며">마치며</h2>
<p>단과반의 두 과정 중 마지막 과정에 진행한 프로젝트였다. 배운 내용들을 최대한 많이 사용해보고 싶었지만 그런 주제를 찾기가 여간 쉽지 않았다. 그래서 추상 클래스, 인터페이스(+마커), 내부 클래스, 익명 클래스, API 등 사용해보지 못 한 것들에 아쉬움이 남긴 하지만 예외처리, JCF, SWING, 파일 입출력을 사용할 수 있어서 그래도 좋은 경험이었다. 7개의 조 중에서 1등으로 결과를 마무리 했다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[CoinBank, 모의 가상화폐 거래소, 2019]]></title>
            <link>https://velog.io/@noly-poly/coinbank</link>
            <guid>https://velog.io/@noly-poly/coinbank</guid>
            <pubDate>Mon, 24 Oct 2022 06:03:40 GMT</pubDate>
            <description><![CDATA[<h2 id="프로젝트-개요">프로젝트 개요</h2>
<blockquote>
</blockquote>
<ul>
<li><strong>주제</strong>:
모의 가상화폐 거래소</br></li>
<li><strong>플랫폼</strong>:
데스크탑 응용 프로그램</br></li>
<li><strong>기간</strong>:
전체 기간: 19.05.03 ~ 19.05.08, 6일</br></li>
<li><strong>인원</strong>: 2명<ul>
<li>👨‍💻 🤚: 시세 변동 알고리즘, 코인 충전, 매수 &amp; 매도, 리포트, 시세 변동 기능
PM, 프로젝트 병합 </li>
<li>👨‍💻: 로그인 기능</li>
</ul>
</li>
</ul>
</br>

<h2 id="프로젝트-핵심-기능">프로젝트 핵심 기능</h2>
<ol>
<li><p>사용자는 로그인을 한다.</p>
</li>
<li><p>금액을 선택해 충전함으로써 난이도를 조절한다.</p>
</li>
<li><p>코인의 종류가 5가지가 있다.</p>
</li>
<li><p>코인의 시세를 확인할 수 있고 시세의 변화가 있다.</p>
</li>
<li><p>코인과 개수를 선택하여 매수, 매도한다.</p>
</li>
<li><p>종료하기 전 리포트를 확인할 수 있다.</p>
</li>
</ol>
</br>

<h2 id="프로젝트-기술-스택">프로젝트 기술 스택</h2>
<ul>
<li><p>Java</p>
</li>
<li><p><a href="https://ko.wikipedia.org/wiki/%EC%8A%A4%EC%9C%99_(%EC%9E%90%EB%B0%94)">javax.Swing</a></p>
</li>
</ul>
</br>

<h2 id="프로젝트-미리보기">프로젝트 미리보기</h2>
<p><img src="https://velog.velcdn.com/images/noly-poly/post/06f02df3-a602-40ee-bec2-ddd45d0625dc/image.png" alt="">
<img src="https://velog.velcdn.com/images/noly-poly/post/b4775847-9fb9-46d5-bca1-868666e60f65/image.png" alt=""></p>
<p><a href="https://github.com/JISU-YANG/2019_CoinBank">Github</a></p>
</br>

<h2 id="keep">Keep</h2>
<h3 id="1-예외상황에-대한-처리">1. 예외상황에 대한 처리</h3>
<p>기본적으로 해당 조건이 아님에도 접근하는 경우들을 고려해서 안내 Pop-up이 나올 수 있도록 수정해두었다. 지금 와서 생각해보면 애초에 그런 버튼들을 비활성화해두는 편이 더 낫겠다는 생각이 들지만, 모든 예외 상황에 대한 대처가 반영이 된 점 자체는 잘했다는 생각이 든다.</p>
<p><img src="https://velog.velcdn.com/images/noly-poly/post/35597c3a-1b50-401e-8f83-d64201a46a36/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/noly-poly/post/1f4b72a0-cf17-4893-887e-a5adf5d4a016/image.png" alt=""></p>
<p>코인의 매수와 매도의 경우 사용자가 숫자를 입력하여 개수를 설정할 수 있는데, 그 부분에 문자열을 입력할 경우 Integer.parseInt()의 파라미터 타입이 일치하지 않으므로 프로그램이 다운되는 것을 막기 위하여 try-catch 문을 적용해두었다. </p>
<p><img src="https://velog.velcdn.com/images/noly-poly/post/7758f13b-b259-49e4-b744-db2c0f6cd095/image.png" alt=""></p>
<hr>
<h3 id="2-시세-변동-알고리즘">2. 시세 변동 알고리즘</h3>
<p>각각의 코인마다 다른 변동 폭을 가졌으면 했고, 그 수치에 대한 부분을 쉽게 컨트롤할 수 있었으면 했다. 마땅한 레퍼런스를 찾을 수 없어서 자체적으로 고안해보기로 하였다. 그래서 일단 코인 객체가 생성될 때 변동 폭을 파라미터로 받았다. 매수, 매도 시에 변동이 이루어지는 구조상 저렴한 코인을 매수, 매도하면서, 시세 변동의 차익을 보는 편법을 막기 위해 개별적으로 시세 변동이 이루어질 수 있도록 객체의 메소드로 구성했다.
<img src="https://velog.velcdn.com/images/noly-poly/post/82303610-5f4c-44a8-bef6-7a7af7fbf3dc/image.png" alt=""></p>
<p>단순히 원래 가격을 랜덤으로 변경하기에는 항상 들쭉날쭉하게 가격 차이가 나는 문제가 발생했다. 고민 끝에 그 편차를 미세하게 조절하기 위해 최소, 최대 가격 폭을 가정했다. 최소 가격에 개발자가 지정한 수준의 변화가 이루어진 값을 구한 뒤 더함으로써 이에 대한 부분을 해결했다.</p>
<p><img src="https://velog.velcdn.com/images/noly-poly/post/f97e3482-7455-43f5-aff5-db164218db65/image.png" alt=""></p>
<h2 id="problem--try">Problem &amp; Try</h2>
<h3 id="1-실시간-시세-변동">1. 실시간 시세 변동</h3>
<p>시시각각으로 변동되는 코인의 시세를 원했지만, Thread에 대한 개념을 모를 때라서 결국 작동되는 지점을 직접 정해주기로 했다. 그나마 자연스럽게 매도, 매수 직후로 설정하고 메소드가 이어 작동하게 하였다. </p>
<p><img src="https://velog.velcdn.com/images/noly-poly/post/0c78f8db-0294-46a1-bda1-bfb7d3508203/image.png" alt=""></p>
<hr>
<h3 id="2-주석-대신-구체적인-변수명-메서드-명으로">2. 주석 대신 구체적인 변수명, 메서드 명으로.</h3>
<p>변수명을 짓는 일은 누구에게 물어도 어려운 일이다. 이 프로젝트를 할 때 나는 최대한 디테일인 부분보다는 상징성에 가깝게 변수명을 지었다. 그러고 로직과 변수에 대한 주석을 철저하게 써놓았다. 만약 변수명에 오히려 디테일한 부분을 나타낼 수 있는 단어로 사용하였다면, 굳이 주석으로 남길 필요도 없었을 것이란 생각이 인제 와서 든다. 그리고 제대로 된 명명법도 알지 못해서 프론트에서 사용하던 _(언더바) 스타일을 사용하고 말았다.</p>
<p><img src="https://velog.velcdn.com/images/noly-poly/post/72bb1f55-be84-4675-ae3a-0a7f127d865b/image.png" alt=""></p>
<hr>
<h3 id="3-단일-책임의-원칙-클래스-분리가-아쉽다">3. 단일 책임의 원칙. 클래스 분리가 아쉽다.</h3>
<p>모든 시스템이 Java 안에서 이루어져야 하므로 고충이 있었다. 나름의 MVC 모델에 대해 들은 적이 있었기 때문에 사용자의 지갑 데이터 같은 경우는 분리해두었지만, 오히려 메시지 클래스라던가, 유틸리티 클래스를 분리하지 않고 프로세스 클래스에 둔 것은 상당히 아쉽다.</p>
<p><img src="https://velog.velcdn.com/images/noly-poly/post/e76602b8-9d57-4ea3-a70b-d11a3d462925/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/noly-poly/post/945b534b-6dfc-4bca-b87a-b18bdb64d75c/image.png" alt=""></p>
<h2 id="마치며">마치며</h2>
<p>직접 코드를 처음부터 끝까지 작성해서 만들어보는 첫 프로젝트이기 때문에 너무 설레었고 특별한 경험이었다. 그것도 Java라는 매력적인 언어로 로직과 연산도 함께했고 웹 서비스가 아닌 자체 프로그램 형태로 결과물이 나온 것도 신기했다. PM으로서는 &quot;팀 프로젝트는 조별 과제와 같은 것인가?&quot;라는 생각이 들며, 꼭 이미 알고있는 것이 아니더라도 도전해보게끔 비전을 공유하는 능력을 어떻게 길러야 하나 고민이 된다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[제대로 개발을 배워보는게 어때?]]></title>
            <link>https://velog.io/@noly-poly/ep2</link>
            <guid>https://velog.io/@noly-poly/ep2</guid>
            <pubDate>Wed, 19 Oct 2022 03:31:20 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/noly-poly/post/8ddb1e40-962b-43b0-8068-76765d48d2e3/image.png" alt=""></p>
<h3 id="인생역전-이제-당신의-것-입니다">인생역전, 이제 당신의 것 입니다.</h3>
<p>열심히 만든 서비스의 자금줄이 실패로 돌아가 약간의 상실감과 일상으로 돌아가려 하던 찰나, QA를 도와줬던 한 친구에게 연락이 왔다. 페이스북의 한 광고에 관해 이야기해주었다. 국비 지원을 받아 무료로 학원에서 개발자 과정을 훈련 받으며 생활비를 주는데 취업률 또한 엄청난 다소 의심할 여지가 많은 이야기였다.</p>
<p>며칠을 고민하다가 확실한 것만 생각해보기로 했다. 나는 직전 프로젝트에서 제대로 배워볼 기회가 없어 자유롭지 못했고 시간을 너무 낭비했다. 반신반의한 마음을 가지고 학원들을 알아보았다. 학원의 인지도, 교통편, 규모를 비교해서 몇 군데를 추린 후 전화 상담했다상담을 했다. 그중 한 곳만 <del>알고 보니 큰 의미가 없었다고 하는</del> 5문제의 테스트를 통과해야 참여할 수 있는 구조였는데 학원을 결정하는 데 큰 영향을 미쳤다.</p>
<p>통과하고 나서 알게 된 사실인데 나는 사실 iOS App를App을 개발하는 Swift를 배우고 싶었다. 모바일 웹의 애로사항을 너무 잘 알고 있었기 때문이다. 하지만 Swift를 배울 수 있는 과정은 없었고, 6개월이라는 기간 안에 전공자들과 함께 따라가는 게 쉽지 않을 거라며 내가 낯설지 않은 &#39;웹 개발자 양성 <del>및 빅데이터 분석</del>&#39; 과정을 권하셨고 나도 동의했다. 그리고 그 당시에는 영업이라고 생각이 들었지만, Java 단과반을 강력하게 추천하셨는데, 지금 돌이켜보아 그 결정을 하지 않았더라면 개발자의 길을 유지할 수 있었을까 생각된다.</p>
<h3 id="귀인이-되어주신-단과반-강사님">귀인이 되어주신 단과반 강사님</h3>
<p>긴장되는 마음으로 강의실로 들어서 자리에 앉았다. 강의가 시작되고 강사 소개를 하시는 도중 이미 인지부조화가 왔다. 컴퓨터 관련 종사하시는 분들은 감정 표현에 인색하고 딱딱하다는 나의 편견을 단번에 부수어 주었다. 오히려 수강생들 앞에서 조증이신 게 아니니까 하고아닌가 하고 의심될 정도였다. 이후 강의 내내 느꼈지만, 역사를 설민석 님에게 배우는 기분이었다. 강사님이 틀리거나 어려운 부분을 내가 질문하면 본인이 너무나 행복해하며 기뻐하셨다. 나도 덩달아 신이 날 수밖에 없었다.</p>
<p>그리고 강의 방식은 이런 식으로 이루어졌다. 바로 기억난 순간이 반복문에 대해 배울 때였는데, System. outSystem.out. println(&quot;Hello.println(&quot;Hello Java World&quot;); 을World&quot;);를 10번을 작성하라고 하셔서 영문도 모른 채 작성했다. 그러시고는 해맑게 웃으시면서 사실, 줄을 복사하는 기능이 있다며 단축키를 알려주셨다. 이제 100번을 입력하라고 하셨다. 다들 황당해하거나벙찌거나 열심히 단축키를 눌렀다. 황당해한벙찐 사람들을 위해 다들 시작하자고 말씀하시더니, 농담이라고 하시면서 그래서 우리가 개발 언어를 배우는 것이라고 말씀하시며 그제야 for 문의for문의 사용법을 알려주셨다.</p>
<h3 id="java-universe">Java Universe</h3>
<p>나는 합리적인 것을 제일 중요하게 생각한다. 그래서 왜 해야 하는지가 굉장히 중요하다. 그런데 왜 그래야 하는지, 개발 언어로 얼마나 편리해지는지를 알려주신 것이다. 변수의 타입에 대해 배울 때 &#39;공간 = 값&#39;이라는 개념을 많은 단과반 수강생들이 헷갈렸다수강생들이 햇갈려했다. 신발을 <del>신발장이 아닌 음식을 놓는</del> 프라이팬에 올리면 안 되듯이, 값들은 각자 그에 맞는 공간이 필요하다는 것을 여러 상황을 제스쳐와 함께 설명해주셨다. 개념이 머릿속에서 시각화된 그날, 나의 Java 세계관(Universe)은세계관(Universe)는 시작되었다. 그 이후에도 개념들이 헷갈리거나 이해가 안 되고 어려운 것이 생겼을 때 강사님을 따로 찾아가면 종종 &quot;개발 언어도 결국 사람이 쓰는 언어이고, 언어는 텍스트가 아니야. 문화야. 느껴봐!&quot;라고 하신 것을 보아 의도하신 일인가 싶다. 아직도 느끼는 것이지만 그렇게 구축된 세계관 안에서는 모든 것들이 일관적인 태도를 취한다. 다른 수강생분들에게 이런 것들을 순화하지 않고 이야기하면 나를 잡아, 변태라고 불렀다.</p>
<p>한 번은 프로젝트를 한다. 도저히프로젝트를 하다. 도저히 해결되지 않아 온몸을 비틀며 괴로워하고 있었다. 그때 강사님께서 전부를 향해 말씀해주셨다. &quot;여러분 지금 막막하고 역경이 닥친 것 같죠. 저는 그럴 때 기뻐요. 반드시 성장이 온다는 신호이거든요!&quot; 이 말씀은 개발이 한정되지개발을 한정되지 않고 나의 인생에 많은 영향을 미쳤다.</p>
<p>나의 되고 싶은 롤모델 개발자이셨고 영향을 많이 받은 만큼 많이 융화되었다고 생각했지만, 국비 과정 기간에 우연히 계단에서 마주쳤을 때 &quot;~이런 프로젝트를 하고 있습니다. 너무 어렵네요.&quot;라는어렵네요.&quot; 라는 나의 푸념에 &quot;아우 너무 맛있겠다. 강사만 아니면 저도 먹고 싶네요!&quot;라는 대답을 하셨고, &quot;Java가요???&quot;라고 되물을 수밖에 없었다. 나는 아직 한참 멀었나 보다.</p>
<h3 id="부족한-배경지식">부족한 배경지식</h3>
<p>한창 개발이 너무 즐거웠다. 집에 가는 길에도 신호등을 보아도, 지하철을 보아도 객체로 보였고, Class로 만든다면 어떤 Method 들로Method들로 Logic을 구성할까 상상했다. 그러다 MVC 모델로 펼쳐보기도 했다.</p>
<p>Java 단과반 과정이 끝나고 국비 과정이 시작되는 날. 다시 한번 나에게 인지부조화가 왔다. 다른 행성에 온 것 같았다. 강사님 한 마디에 내가 모르는 용어가 2개씩 나왔고, 흐름을 놓치면 다른 내용으로 변해있었다. 정신을 차리고 방법이 필요하다고 생각해 당장 알 수 없는 용어와 개념들은 구글 워드에 다 적어두었고, 최대한 맥락을 이해하려고 노력했다. 그날 교육이 다 끝난 후에 작은 자습실에서 적어두었던 것들을 열심히 알아보고 이해하고 설명을 작성하였다. 집에 오는 버스와 지하철에서 정리해놓은 것을 보고 복습했다.</p>
<p>현실적으로 6개월이라는 기간 안에 커리큘럼을 전부 진행하려면 어쩔 수 없는 부분이기도 했다. 막히는 부분이 적어야 하나라도 덜 시간을 빼앗겨 연쇄적으로 놓치지 않을 수 있었다. 포기할 것이 아니면, 더 부단히 노력하는 수밖에. 과정 시작 일주일 만에 앞으로 해야 할 전체적인 흐름에 대한 핵심들을 미리 알게 되었는데, 따라가기 벅차 집에 가는 버스에서 창밖을 보며 참 슬퍼했던 기억이 난다.</p>
<p><img src="https://velog.velcdn.com/images/noly-poly/post/73d8aaae-f271-4ea4-936c-a3e966f86707/image.jpg" alt=""></p>
<h3 id="알려줘서-나준다">알려줘서 나준다</h3>
<p>다행인지는 모르겠지만 그렇게 과정을 힘들어하는 사람이 나뿐만은 아니었다. 끝나고 남아서 공부하는 인원들이 하나둘씩 늘어나다가 결국 작은 자습실로는 인원을 감당할 수 없어 학원 근처 카페로 옮기게 되었다. 그리고 주로 질문들에 대한 대답을 내가 해 줄 수 있어서 하다 보니 내가 공부할 시간이 줄어들어 고민이 되었다.</p>
<p>결정을 내렸다. 모든 질문에 대답해주기보다는 고민의 시간이 더 필요해 보이는 분은 그렇게 말씀드리고 넘어갔다. 그리고 질문을 받으며 내가 모르는 부분이 있기도 했고, 그것을 이해할 수 있도록 쉽게 설명하는 것은 다른 수준의 이해도를 요구했기 때문에, 나 또한 도움이 된다는 생각으로 더 많이 고민하며 공부하게 되었다.</p>
<p>3개월 정도 지난 후에 강사님과의 면담에서 &quot;수강생들이 내가(강사가) 아닌 너에게 질문을 구하는 것이 좋게 보이지도 않고, 너한테 도움이 안 된다. 이해가 안 된다.&quot;라고 말씀을 해주셨는데 어떤 의도에서 하신 말인지는 알지만, 같이 고생하며 공부한 시간이 떠올라 화도 나고, &quot;그렇지 않다고, 오히려 도움이 된다고&quot; 떳떳하게 말하고 싶은 오기도 생겼다.</p>
<p>더 큰 책임감을 느끼고 강사님의 이쁨을 받던 전공자분들과 비슷한 수준이 되려고 노력했고, 더 가까이 지내려고 노력했다. 서로 모르는 것들과 재밌는 문제가 발생했을 때 공유도 했다. 강사님께도 인정을 해주셨고 과정 수료식에서 선행상을 받게 되었다.
<img src="https://velog.velcdn.com/images/noly-poly/post/7845a73a-8d25-4d3e-91d3-f89321fdc29b/image.jpg" alt=""></p>
<h3 id="벽이-아니라-계단이라고요">벽이 아니라 계단이라고요?</h3>
<p>Java와 HTML/CSS, JavaScript를 배우고 나서 DB를 마주했을 때 지금까지 내가 배웠던 개발 언어와 너무 다르다 보니 이해가 쉽지 않았다. 특히 이론적인 부분도 그러했다. 객체 지향적이지객체지향적이지 못한 RDBMS만의 성향이 또 있었다. 그리고 대표적으로 abatisiBATIS, Batista를MyBatis를 처음 배울 때가 생각이 나는데, 도무지 머릿속에 그림이 그려지지 않아 황당해했었던 순간이 몇 번 있다. &quot;<strong>그냥</strong> 그렇게 쓰면 돼.&quot;라고돼.&quot; 라고 생각하고 넘어가는 걸 참 못 했다. 어째서, 왜 그래도 되는지가 나에겐 너무 중요했던 것 같다. 그래서 계단의 높이가 너무 높게 느껴졌다. 좀 오래 걸렸지만, 완전히 개념적인 분리를 했고, JDBC부터 흐름에 대한 부분을 다시 이해를 완벽히 이해하고 나서 주로 사용하게 될 부분에 집중해서 알아보았던 것 같다.</p>
<p>다른 사람은 쉽게 넘어갔는데 나는 그러지 못한 모습을 보고 있을 때면 개발적인 재능에 대한 의심이 생기곤 했다. 괴로워하며 과정을 계속 따라갈 수 있을지 걱정이 들곤 했는데, 그때 알게 된 것이 예를 들어, 4레벨을 내가 이해하지 못하더라도 5레벨을 경험하면서 4레벨의 이해도 따라오는 순간이 있다는 것이었다. 이런 경험이 반복된 후부터는 다행히도 낙관적인 마인드를 갖기가 좀 수월해졌다.</p>
<h3 id="pm으로서의-역량">PM으로서의 역량</h3>
<p>대망의 최종 프로젝트는 원래 기한이 30일 정도였다. 강의와 병행해서 진행됐는데, 인원이 나까지 6명이었다. 전부 실력이 천차만별인 0년 차 개발자로 구성이 되어 PM으로서 어려운 부분이 많았다. 투표를 많이 활용하여 의견에 대한 충돌은 다소 많지. 않았다. 추가로 확실한 근거가 있는 주장에 대해서는 더 설득과 권유로 이어졌다. 이번 경험으로 되돌아보면 아래의 부족한 점들을 깨달을 수 있었다.</p>
<ol>
<li>각자 자신이 어느 정도 역량을 낼 수 있는지 몰랐었고, 최대한 지금까지 봐온 모습으로 담당 기능을 추천하고 배정했다.</li>
<li>담당 기능에 대해 어려움을 겪거나 기한에 대한 문제가 있었을 때 어떻게 할지 미리 고민해보지 못했다.</li>
<li>프로젝트가 포트폴리오로 사용되어야 했기 때문에 인원이 많은 만큼 스케일을 늘려야 했다.</li>
<li>서류가 완성도가 높으면 그렇게 개발로 이어질 줄 알았다.</li>
<li>GitHub에Github에 대해 초반에 시간이 들더라도 강제했어야 했다.</li>
</ol>
<p>결론적으로 아쉽지만 설정한 기간을 계속 못 맞추고 취업 준비로 이탈자까지 발생하는 상황이라 NAWABNAWA 프로젝트는 미완성으로 종료하게 되었다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[History, 약속 관리 노트, 2018]]></title>
            <link>https://velog.io/@noly-poly/history</link>
            <guid>https://velog.io/@noly-poly/history</guid>
            <pubDate>Tue, 18 Oct 2022 14:59:15 GMT</pubDate>
            <description><![CDATA[<h2 id="프로젝트-개요">프로젝트 개요</h2>
<blockquote>
</blockquote>
<ul>
<li><strong>주제</strong>:
약속 관리 노트</br></li>
<li><strong>플랫폼</strong>:
모바일 웹 사이트</br></li>
<li><strong>기간</strong>:
전체 기간: 18.08.17 ~ 19.04.23, 9개월
Prototype: 18.08.17 ~ 18.08.25
Beta 1.0: 18.09.20~ 18.09.20
Beta 1.2: ~ 18.10.12
Beta 1.3: ~ 19.01.19
Beta 1.5: ~ 19.04.23</br></li>
<li><strong>인원</strong>: 2명<ul>
<li>👨‍💻 ✋: PM, 개발, UI/UX, 기획</li>
<li>👨: 기획, 마케팅</br></li>
</ul>
</li>
<li>진행 방식:</li>
</ul>
<ol>
<li>브레인 스토밍을 통해 핵심 기능 세부화하기</li>
<li>Prototype 제작 후 개선점 고안하기 (Prototype ~ Beta 1.2)</li>
<li>Client를 찾아 요구사항 반영하기 (Beta 1.3 ~ Beta 1.5)</li>
</ol>
</br>

<h3 id="프로젝트-핵심-기능">프로젝트 핵심 기능</h3>
<ol>
<li><p>유저는 자신의 약속 리스트를 가진다.</p>
</li>
<li><p>약속 단위로 일정에 대해 기록할 수 있다.
(필수: 날짜, 장소, 선택: 참여자, 지출 금액, 지출 방법)</p>
</li>
<li><p>장소 혹은 참여자로 검색할 수 있다.</p>
</li>
<li><p>지출에 대한 리포트(통계)를 볼 수 있다.
4-1. 지출한 방법 별로 나누어 볼 수 있다.
(신용카드, 체크카드, 현금지출)</p>
<p>4-2. 기간에 대한 범위를 선택할 수 있다.
(일, 주, 월, 년)</p>
</li>
</ol>
</br>

<h3 id="프로젝트-기술-스택">프로젝트 기술 스택</h3>
<ul>
<li><a href="https://rhymix.org/">Rhymix (Open Source CMS)</a></li>
<li>PHP</li>
<li>HTML/CSS</li>
<li>JavaScript</li>
<li>jQuery</li>
</ul>
</br>

<h3 id="프로젝트-미리보기">프로젝트 미리보기</h3>
<p><a href="https://owaw.net/"><del><u>Demo</u></del></a> <a href="https://github.com/JISU-YANG/2018_History"><u>Github</u></a> </p>
<img src="https://velog.velcdn.com/images/noly-poly/post/27bb9997-194d-4635-aa10-e533844d599e/image.gif"/>

<img src="https://velog.velcdn.com/images/noly-poly/post/f62b434b-775d-48fa-ad36-ff918e29db06/image.png"/>

</br>

<h2 id="keep">Keep</h2>
<h3 id="1-jquery를-잘-활용한-것-같다">1. jQuery를 잘 활용한 것 같다.</h3>
<p>JavaScript를 자유롭게 구사할 수 있었다면 덜 고생하고 더 깔끔하게 코드가 구현될 수 있었던 것은 사실이지만, 그 당시 나는 JavaScript를 잘 알지 못 했고, 같은 기능을 구현할 수 있는 두 가지 방법 중에 더 직관적인 쪽을 선택했다.</p>
<ul>
<li>Element의 값 입력<pre><code class="language-javascript">// 버튼으로 달력의 날짜를 컨트롤 하기 위해 값을 입력하는 부분
$(&quot;#dateButtonTomorrow&quot;).click(function() {
  var text = &quot;{date(&#39;Y-m-d&#39;,$tomorrow)}&quot;;
  $(&quot;#date&quot;).val(text);
});</code></pre>
</li>
<li>더 다양한 함수 사용<pre><code class="language-javascript">// 자연스러운 애니메이션 전환을 위한 delay(), switchClass()
$(function() {
  $(&quot;#When-SB&quot;).on(&quot;click&quot;, function() {
      $(&quot;svg.When-LB&quot;).delay(100).switchClass(&quot;When-LB&quot;, &quot;When-LB-Off&quot;, 200, &quot;easeInOutCubic&quot;);
  });
});</code></pre>
</li>
</ul>
<hr>
<h3 id="2-rhymix-시스템에-대한-이해가-늘었다">2. Rhymix 시스템에 대한 이해가 늘었다.</h3>
<p>이 CMS는 PHP 7.0 이상을 지원한다. 나는 쉽게 레퍼런스를 얻을 수 있는 특화된 문법을 사용했는데, 기능에 대해 명확하게 알지 못한 채 외부자료에 의존한 채로 값을 조절해가며 이해하느라 시간이 많이 소요되었다.</p>
<ul>
<li><p>사용자 변수 가져오기</p>
<pre><code class="language-html">{$oDocument-&gt;getExtraEidValue(&quot;M1_1&quot;)}</code></pre>
</li>
<li><p>조건문 사용하기</p>
<pre><code class="language-php">&lt;!--@if($oDocument-&gt;getExtraEidValue(&quot;M2&quot;)==&#39;만남&#39;)--&gt;
&lt;!--@else if($oDocument-&gt;getExtraEidValue(&quot;M2&quot;)==&#39;음식&#39;)--&gt;
&lt;!--@else--&gt;
&lt;!--@end--&gt;
</code></pre>
</li>
</ul>
<pre><code>
- shrtotime() 함수 사용하기
```php
// 전후 날짜 변수 초기화
{@ $yesterday = strtotime(&quot;-1 day&quot;)}
{@ $tomorrow = strtotime(&quot;+1 day&quot;)}</code></pre><ul>
<li>substr() 함수 사용하기<pre><code class="language-php">// 날짜 표기
{substr($oDocument-&gt;getExtraEidValue(&#39;D1&#39;),2,2)}
.
{substr($oDocument-&gt;getExtraEidValue(&#39;D1&#39;),5,2)}
.
{substr($oDocument-&gt;getExtraEidValue(&#39;D1&#39;),8,2)}
</code></pre>
</li>
</ul>
<pre><code>- 모듈 호출하기
```html
&lt;!-- 작성 --&gt;
&lt;a class=&quot;fas fa-pencil-alt&quot; href=&quot;{getUrl(&#39;act&#39;,&#39;dispBoardWrite&#39;,&#39;document_srl&#39;,$oDocument-&gt;document_srl,&#39;comment_srl&#39;,&#39;&#39;,&#39;yves&#39;,&#39;&#39;)}&quot;&gt;&lt;/a&gt;
&lt;!-- 북마크 --&gt;
&lt;a href=&quot;#&quot; class=&quot;fas fa-star&quot; onclick=&quot;doCallModuleAction(&#39;member&#39;,&#39;procMemberScrapDocument&#39;,&#39;{$oDocument-&gt;document_srl}&#39;);return false;&quot;&gt;&lt;/a&gt;
&lt;!-- 삭제 --&gt;
&lt;a class=&quot;fas fa-trash-alt&quot; href=&quot;{getUrl(&#39;act&#39;,&#39;dispBoardDelete&#39;,&#39;document_srl&#39;,$oDocument-&gt;document_srl,&#39;comment_srl&#39;,&#39;&#39;,&#39;yves&#39;,&#39;&#39;)}&quot;&gt;&lt;/a&gt;
</code></pre></br>

<h2 id="problem--try">Problem &amp; Try</h2>
<h3 id="1-게시판의-구조-변형">1. 게시판의 구조 변형</h3>
<p>아무래도 전문적인 지식이 부족한 상태라 원래 시스템을 건드리는 것은 위험하다고 생각이 들어서 비효율적이지만 대처를 하는 형태로 접근을 했다. 주로 표시하는 부분을 없앤 후 보이지 않게 그에 대해 기본값을 설정해주었다.</p>
<ul>
<li>본문 값이 필수인 부분을 임의 값으로 대체하여 저장하기<pre><code class="language-html">&lt;input type=&quot;hidden&quot; name=&quot;content&quot;  value=&quot;.&quot;&gt;
</code></pre>
</li>
</ul>
<pre><code>- 공개, 비공개 선택 상자 자동으로 체크하기
```javascript
document.onclick = function(e) {
  if (!e) e = window.event;
  var el = e.target || e.srcElement;
  if (el.type === &quot;checkbox&quot;) {
    if (el.checked){
      el.nextSibling.className += &quot; checked&quot;;
    }else{
      el.nextSibling.className = el.nextSibling.className.replace(/\bchecked\b/, &quot;&quot;);
    }
  }
}</code></pre><hr>
<h3 id="2-구조적-문제">2. 구조적 문제</h3>
<p>MVC에 대한 개념, Framework 에 대한 정보가 없었어서 화면에 대한 수정에 상당히 애를 먹었다. 예를 들어, Header나 Footer에 대한 간단한 수정이 있어도 모든 파일의 코드를 수정을 해야만 했다.</p>
<p>중반부에 이 부분에 대한 해결 방법이 <del>절대</del> 있을거라고 믿고 검색과 삽질의 반복을 통해 include 태그를 사용할 수 있게 되었다. 그래서 한가지 주요한 책임 단위로 나누어, 최종적인 코드에는 include의 조합이 작성되었고, 재사용성을 높일 수 있었다.</p>
<pre><code class="language-html">&lt;include target=&quot;/a.html&quot; /&gt;</code></pre>
<hr>
<h3 id="3-qa-요구사항">3. QA 요구사항</h3>
<p>지인들에게 QA를 부탁한 후 가장 많이 이야기를 들었던 것이 이미 작성했던 약속에 대해 수정을 할때 날짜가 자동으로 입력이 되어있지 않은 부분을 꼽았다. 인지하고 있었지만 text가 아닌 date는 조금 까다로웠다. 정보를 찾기가 쉽지 않아 우선순위에서 미루었지만 QA 이후 해결하기로 마음먹고 다른 형태의 레퍼런스를 참고하여 개선할 수 있었다.</p>
<ul>
<li>약속 수정 시 기존 값 불러오기<pre><code class="language-html">&lt;div class=&quot;writeInputBasic&quot;&gt;
  &lt;input id=&quot;date&quot; type=&quot;text&quot; name=&quot;extra_vars1&quot; id=&quot;when&quot; cond=&quot;!$oDocument-&gt;getExtraEidValueHTML(&#39;when&#39;)&quot; /&gt;
  &lt;input id=&quot;date&quot; type=&quot;text&quot; name=&quot;extra_vars1&quot; id=&quot;when&quot; value=&quot;{zdate(str_replace(&#39;-&#39;,&#39;&#39;,$oDocument-&gt;getExtraEidValue(&quot;when&quot;)), &#39;Y-m-d&#39;)}&quot; cond=&quot;$oDocument-&gt;getExtraEidValueHTML(&#39;when&#39;)&quot; /&gt;
&lt;/div&gt;</code></pre>
</li>
</ul>
</br>

<h2 id="마치며">마치며</h2>
<p>사실, 할 수 있는 최선을 다했지만 능력적인 한계에 부딪힌 부분이 많은 프로젝트였다. 온전히 Architecture를 설계할 수 있는 자유로움이 없기 때문에 CMS에 의존적일 수 밖에 없었고, 추가적으로 프로그래밍 언어에 대한 지식이 얕아서 시간 대비 고생을 많이 했다.</p>
<p>이번 프로젝트를 진행하면서 코드 한줄을 해결하지 못해 라디오와 함께 밤을 새면서도 해결한 순간 느낀 그 도파민을 잊을 수가 없다. 그 기억이 나를 개발자의 길로 향하게 한 불쏘시개🔥가 되었다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[제가 개발을 하라고요?]]></title>
            <link>https://velog.io/@noly-poly/ep1</link>
            <guid>https://velog.io/@noly-poly/ep1</guid>
            <pubDate>Tue, 18 Oct 2022 12:50:35 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/noly-poly/post/939780de-83ad-4b33-a7d8-73a5dff545dd/image.png" alt=""></p>
<h3 id="나는-개발자이기-전에-발명가가-꿈이다">나는 &#39;개발자&#39;이기 전에 &#39;발명가&#39;가 꿈이다.</h3>
<p>닷컴버블 때 성장한 대기업들의 서비스를 이용하면서 자랐다. 학교 도서관에서 우연히 읽은 책에서 IBM이란 골리앗을 이긴 다윗과 같은 Apple이라는 회사에 대해서 알게 되었다. 그것을 주도한 사람은 &#39;스티브 잡스&#39;라는 인물이었다. 마침, 또래 사이에서 아이팟 터치가 상당히 유행이었다. 그러다 <a href="https://www.youtube.com/watch?v=FGg90wMuMpw&amp;ab_channel=regor">2007년에 아이폰을 처음 소개하던 스티브 잡스의 프레젠테이션 영상</a>을 보게 되었다. 내 꿈은 그때 결정되었다. 사람들이 필요하지만 상상도 못 했던 것을 출시하면서 위트있고 마술을 보이는 것과 같이 발표하는 모습에 나는 홀린 듯이 매료되었다.</p>
<p>그렇게 &quot;32살에 스티브 잡스와 같이 사람들의 라이프 스타일을 크게 개선할 수 있는 솔루션을 제공하는 기업의 CEO가 되겠다.&quot;라는 나의 목표를 설정했다.</p>
<h3 id="미래의-내가-필요한-능력">미래의 내가 필요한 능력</h3>
<p>32살이라는 나이는 취업 후, 결혼 전 충분히 능력을 갖추었을 때를 의미하지만, 구체적인 나이는 계획을 세우는 데 상당히 도움이 된다. 나중에 바꾸게 될지라도. 그때까지 내가 갖추어야 할 능력은 경영, 기획, 브랜딩, 마케팅, 디자인, 엔지니어를 생각해볼 수 있었다.</p>
<p>일단 도서관에 있는 경영에 대한 책을 하루에 2권씩 읽으며 메모하고 하루를 그렇게 보냈다. 몇 달이 걸리지 않아 도서관에 있는 책들을 전부 읽게 되었고, 나는 시험해보고 싶었다.</p>
<p>그래서 비전을 함께할 팀을 만들고 캠페인과 작은 프로젝트를 기획해서 친구들을 끌어들였다. 프로그래밍 언어를 조금 알고 있는 팀원을 모집할 수 있게 되어 독특한 컨셉의 커뮤니티를 여러 형태로 도전을 했고 3년이 흘렀다. 들인 고생과 결과물은 있었지만 특별한 성과는 없었다. 그것을 견디지 못하고 그 팀원은 결국 <del>탈주</del>소식을 감추었다.</p>
<h3 id="필요한-사람을-찾기-힘들면-우리가-되어보자">필요한 사람을 찾기 힘들면 우리가 되어보자</h3>
<p>내 또래에 어느 한 분야의 세미-전문가 수준의 능력을 지닌 사람은 드물었기에 같이 배워나가기로 결심했다. 프리랜서 디자이너로 활동하던 시기라 다른 크루원들과 가장 먼저 디자인, 브랜딩에 대한 스터디를 각각 12주, 8주 동안 진행했다.</p>
<p>그렇지만 프로젝트에 대한 아이디어를 도전해 볼 수가 없어서 아쉬워하던 무렵에 다른 팀원이 나에게 &quot;네가 개발을 해보는 건 어때? 컴퓨터는 우리 중에서 제일 잘 알잖아.&quot;라는 말을 건네었다. 그 상황에서는 가능성이 제일 높은 말이기도 했지만 나는 개발이란 분야에 엄청난 벽이 존재했었다.</p>
<h3 id="내가-개발을-겁내던-이유">내가 개발을 겁내던 이유</h3>
<p>고등학교 3학년 시절 종로산업정보학교에서 컴퓨터 정보과를 이수했다. 응용 프로그래밍, 웹 프로그래밍, 그래픽디자인, 데이터베이스 4가지 강의로 이루어져 있었는데 수준이 그렇게 높지 않았다. 응용 프로그래밍에서는 C를 배웠는데 전혀 이해가 가지 않는 것투성이였고것들 투성이였고 다들 그렇듯이 포인터를 만나서 마음이 완전히 떠났다. 시험을 위한 암기만 있었다. 데이터베이스도 &quot;엑셀을 어렵게 쓰기. &quot;라는 인식이 있었고, 웹 프로그래밍은 HTML/CSS를 다루었는데 어깨너머 본 것들이 있어서 수월하게 진행했다. 그래픽 디자인은 말할 것도 없었다. 그렇게 총 100명 정도 과 인원들 사이에서 5등 이내의 성적을 유지했었다. 그리고 중반부가 지나서는 선생님들께서도 수업이 힘든 학생들을 돌아다니며 설명해주는 조교 역할을 맡기어주셨다. 교내외 대회도 참여하며 수상도 해보고 자신이 넘쳐있을 때였지만, 사실 그때는 프로그램을 만들고 로직을 구성하고 이런 것들이 어렵기만 하고 막막하기만 하고 재미가 없었다.</p>
<h3 id="알겠어-일단-해볼게">알겠어. 일단 해볼게</h3>
<p>그때의 기억을 뒤로한 채로 팀원들에게 알겠다고 해보고 전에 있던 팀원이 사용하던 방식을 그대로 물려받았다. 스노보드그누보드, 제로 보드로제로보드로 더 많이 알려진 CMS 계열의 <a href="https://rhymix.org/">RhymerRhymix</a>를 기반하고 있었다. 추가적인 모듈에 HTML/CSS, JavaScript, jQuery, PHP 등을 사용해 프로젝트에 맞게 커스텀 해왔던 것으로 보인다. 좋다. HTML/CSS는 알겠고 나머지는 뭐지? 싶었다. 일단 넘어갔다. 일단 기획한 것을 만들어 가려고 보니 우리가 다뤄야 하는 값들이 있는데 기본적으로 있지 않았다. 구글링을 통해 사용자 변수 기능을 제공하는 것을 찾아냈고 참고 자료들을 따라 하며 기능을 넣었다. 저장까지는 됐는데 그 값을 다루는 과정은 한참을 헤매다가 PHP와 dis로js로 처리한다는 것을 찾아냈다.</p>
<h3 id="한-걸음을-이겨내는-것을-반복하면-정상에-도착한다">한 걸음을 이겨내는 것을 반복하면 정상에 도착한다.</h3>
<p>기본적인 사칙연산을 하는 것조차 문법(+프로그래밍 언어의 구조)을 모르는 상태에서 고행이었다. 잘게 나누어서 하나씩 넣어보고 어떻게 달라졌는지 변화를 관찰해가며 분명 코드는 더 늘어났는데 화면 속의 값은 아무것도 달라지지 않는 기이한 답답함을 느끼며 몇 시간씩을 소모했던 것 같다. 정점은 미관 요소를 더하기 위해 jQuery를 적용하는데 단지 한 부분을 해결하기 위해 9시간이 걸리거나, 다음 화면으로 값을 전달하고 싶은데 방법을 알지 못해 HTTP의 GET 타입 방식으로 값을 넘기고 받아오는 데까지 꼬박 이틀을 밤을 새웠다. 기한도 기한이지만 답답하고 화가 나서 잠이 오지 않았다. <del>그때 흡연량이 정말 많이 늘었고 점점 피폐해져서 찾은 방안이</del> 라디오를 틀어 적막한 공기를 깨는 것만으로도 심적으로도 큰 도움이 되었고 활력이 되었다.</p>
<h3 id="우리가-필요한-것을-만드는-능력">우리가 필요한 것을 만드는 능력</h3>
<p>이제 이 방식으로는 나와 팀이 생각하는 것을 어느 정도 구현할 수 있게 되었는데, 복잡하거나 전문적인 기능은 여전히 불가했다. 현실적인 타협을 보며 팀원들과 서비스를 하나씩 도전해나갔다. 주로 메모와 그것을 활용하는 서비스들이었는데 하루의 중요했던 3가지 순간을 간단히 메모하는 &#39;추억 메모, WAWOWAW&#39;, 커플 디데이와 데이트 장소와 느낀 점을 함께 작성할 수 있는 &#39;커플 다이어리, AviaryLoviary&#39;, 자신의 문화생활을 월간 별로 수집할 수 있는 &#39;월간문화 일기월간문화일기, Library&#39; 서비스를 모바일 웹서비스로 제작하였다.</p>
<h3 id="자금이-되어줄-프로젝트">자금이 되어줄 프로젝트</h3>
<p>그동안에는 완성도와 더불어 동기가 부족했기 때문에 수익화에 대해 리더로서 반대하는 입장이었다. 대신 &quot;우리가 직접 사용하며 보람을 느낄 수 있는 서비스를 만들어보자.&quot;를 제안했다.</p>
<p>그렇지만 이제는 자금에 보탬이 될 만한 서비스를 도전해보아도 괜찮겠다는 생각이 들었다. 그래서 진행한 게 <a href="https://velog.oiio/@newlynoly-poly/history">History</a> 프로젝트이다. 완성되고 발품을 팔러 다니며 느낀 반응은 &quot;잘 모르겠다.&quot;라는 사람이 반, &quot;괜찮은데 아쉽다.&quot;, &quot;아주 좋은데?&quot; 반이었다. 그러다 좋게 봐주신 자산 관리 업체 한 곳이 있었고 고객들에게 제공해주면 소비 습관에 도움이 될 것 같다고 해서 담당자와 한 달 정도 미팅을 가졌다. 결론적으로는 윗선의 승인을 받지 못했다. 너무 아쉽게 마무리가 되었다.</p>
<p>그러던 찰나에 지인에게 &quot;너 <a href="https://velog.io/@noly-poly/ep2">제대로 개발을 배워보는건 어때?</a> 생활비할 수 있는 돈도 준대.&quot;라며 국비지원 과정을 소개받게 되었다.</p>
]]></description>
        </item>
    </channel>
</rss>