ETC/프로젝트

[CrossWord_ActionOnGoogle] CrossWord 프로젝트

지과쌤 2020. 9. 7.
반응형

기획, 설계부터 개발까지 게임을 만들어봤다. 

10월달에 정식 배포를 진행할 예정이다.

 

협업에 미숙하고 처음 다뤄보는것들, 그리고 역할 분담까지 여러모로 순탄치 않은 과정이였지만 처음 기획했던대로 꽤 괜찮은 결과물을 낸것같아 뿌듯했다. 특히 여태껏 무언가 만들어보고 간단하게 정해진 값을 출력하고, 입력받은 값을 출력하는 정도의 프로그램만 만들었었는데 이번에는 정말 깔끔하게 CRUD를 구현하였다는 점에서 기분이 정말 좋았다.

 

본 포스트에서는 백엔드 부분을 중점적으로 다룰 예정이다.

프론트쪽도 많이 공부했지만, 기록으로 남기기에는 아직 부족한점이 많아, 추후 공부를 하며 관련 포스트를 작성해볼 생각이다.


CROSS WORD

첫번째 프로토타입

youtu.be/8iOmDZm33Ew

프로토타입 1

화면 디자인이 초안에 맞춰서 가볍게 나왔고 아직 DB연동이 되지 않은 상황이라 사용자 발화에 따라 인텐트가 제대로 넘어가는지, 미리 설정해둔 값들이 제대로 오가는지 로그를 확인했다.

그리고 미리 설정해둔 정답 단어를 잘 인식하여 다 맞추었을때는 성공 처리가 되는지, 다 맞추지 못했을때는 실패 처리가 되는지도 확인해봤다.

(영상에는 나오지 않지만, 실패시 실패 화면으로도 잘 넘어간다!)

 

두번째 프로토타입

<영상 삽입 예정>

 

현재 따로 영상으로 찍지 못하였다..(막판에 너무 바빠서 까먹었다....) 따로 집에서 테스트 앱을 돌려보려했지만, 서버쪽 경로가 수정되었는지 아무튼 모종의 이유로 앱이 실행되지 않는다. (나 말고 다른 인턴들도) 따라서 시연 영상은 나중에 기회가 된다면 찍어보도록 해야겠다.

따라서 해당 Intent 설명 후, 두번째 프로토타입의 스크린샷을 넣을 예정이다.

 

2020년 8월 31일 기준 두번째 프로토타입에서 구현된것
- Dialogflow 작성
- 프론트 페이지 레이아웃 2차 프로토타입 
- 랭킹화면 스크롤바가 네스트 허브 맥스 기기에서 표시되지 않는 현상 수정
- 게임생성 로직 완성 (완전탐색) 및 게임생성로직 js 포팅
- DB 기본 테이블 생성 및 데이터 CRUD 구현
- JPA - PostgreSQL 연동
- 구글 계정에서 User 정보 가져오기

 

이후 버전

youtu.be/n27xl4pVVmo

youtu.be/VWWfK5P3xgo

 

광고 영상도 나왔다...!

youtu.be/1bmNhnBo0gE

가슴이 웅장해진다....

 

 

 

Google Assistant의 Interactive canvas를 활용한 word cross 게임이다.

 

Spring 프레임워크를 사용하여 JAVA 환경에서 개발을 진행하였다.

 

front는 html, js, css를 사용하였고 js상에서 화면을 구성하였다.

 

back에서는 dialogflow 구조를 기본적으로 따라가고, Spring Data JPA 를 활용해 사내 postgresql과 연동하여 CRUD를 하였다.

 


초안

토의하며 간단하게 그려본 초안

초안을 작성할때 가장 어려웠던점이 우리가 흔히 마주했던 터치 기반 게임이 아닌, 음성인식이 주가 되는 게임인걸 고려해야한다는것이였다.

터치로 씬을 넘어갈때에는 사용자가 방향성을 정하고 입력을 넣는 과정이 굉장히 빠르다. 하지만 음성 입력이 주가되는 구글 어시스턴트에서는 음성을 입력받는 과정과 음성 입력 후 인식하는 과정*이 추가되기때문에 최대한 ux/ui를 직관적으로 만들고, 음성으로 이동할 때, 최대한 중간과정 없이 원하는 지점으로 이동할 수 있게 만들어야한다.

 

(*assistant의 말이 끝날때, 사용자가 "OK GOOGLE" 이라고 말할때만 음성 입력을 받는다...)

 

늘 터치 환경에서 서비스를 이용해왔기때문에 회의를 하다보면 어느샌가 터치 위주로 생각을 하고있어서 계속 음성인식쪽으로 생각을 바로잡느라 애를 먹었던것같다.

 

메인화면 / 모드선택 / 스테이지 및 난이도 선택 / 인게임 / 결과 / 설정 / 랭킹 

 

정도로 씬이 나뉘고 각 씬은 필요에 따라 DB CRUD를 하게된다.

 

초기 기획안
초기 기획안


DB - PostgreSQL

거의 2년전에 배웠던 DB여서 테이블을 만들때 꽤나 고생했다...

처음 제대로(?) DB를 짜보는거라 제대로 해보고싶어서 기본적인 타입부터 차근차근 공부를 해봤었는데 생각보다 고려해야하는 부분들이 꽤 있어 골머리를 앓았었다.

(쓸데없이..? 비효율적으로....?) 재미가 붙어서 쭉 정리해본 PostgreSQL 기본 데이터 타입 정리.

https://earthteacher.tistory.com/41

 

[PostgreSQL] 기본 데이터 타입 정리

PostgreSQL 기본 데이터 타입 정리 Character types / why should we use char instead of varchar? Numeric types Boolean types Temporal types UUID for storing Universally Unique Identifiers Array for sto..

earthteacher.tistory.com

구조도 최대한 깔끔하게 만들어야해서 이런저런 시행착오를 많이 겪었다.

 

도대체...이건...뭐지.....?

 

아무튼 여러 시행착오를 거치면서 꽤 깔끔하게 설계가 되었다.

 

꽤 깔끔하게 된 DB 설계

크게 유저 / 단어 / 힌트 table로 나뉘어진다.

 

DB - User_info Table

유저테이블의 기본키는 유저 E-mail 로 두기로 했다.

google 계정과 연동하여 email 정보를 받아와 테이블에 삽입할 예정이기때문에 기본키 조건을 만족한다고 판단되었다.

생성할때, timestamp를 찍어 생성 날짜를 기록하고, 앱에 접속할때마다(계정 검증 과정에서) 이미 등록되어있는 계정이면 최근접속 날짜를 기록한다.

 

이외 속성들은 default값을 갖게된다.

<default값>
레벨 : 1
경험치 : 0
보유 힌트 : 3
보유 코인 : 5000

테스트중.....원래 내가 1등이였는데 다른친구가 열심히 게임을 하더니 내 경험치를 넘어버렸다..

- 실행시 계정에 로그인되어있는 Google 계정과 user_email 속성의 값을 대조하여 유저 정보가 없다면 새 유저 프로필을 생성한다.

 

- 생성시 account_timestamp가 기록되고, 이후 접속할때마다 visit_timestamp가 기록되며 최근접속시간이 갱신된다.

(현재 최근접속시간이 갱신이 안되는 오류가 있다. 추후 수정예정)

 

- exp, level, coin, hint 등 유저 data 항목 변경시 실시간으로 update된다.

 

DB - Word_info Table

단어테이블의 기본키는 serial 타입으로 정했다. 나는 단어 속성이 기본키 조건을 만족시켜 기본키로 둬야겠다고 생각했지만, 관리의 편리성을 위해 serial 타입의 word_seq 속성을 만들어 기본키로 정하는게 좋겠다는 의견을 받아들였다.

난이도는, 바로 아래 사진에서 자세한 기준을 정하였다.

단어 난이도

위 사진처럼 기준을 정하였고, A1은 1, A1+A2는 2, A1+A2+B1는 3... 이렇게 순차적으로 숫자를 부여하여 난이도를 각 단어별로 부여하였다.

그리고 단어를 쓰지않을때 삭제하는것이 아닌 db상에 남겨두고싶어 boolean 타입으로 flag를 찍어놓았다.

사용하지 않을때엔 false로 바꾸어주어 따로 관리하면 된다.

단어 테이블

 - 2020년 8월 25일 기준 약 5032개 단어를 등록해놓았다.

 

- 각 단어별 난이도를 구분지어 DB에 저장하였다. 

 

- flag는 추후 단어를 쓰지 않을 경우 삭제가 아닌 false로 만들어 구분을 지어놓기위해 사용하였다.

 

 

 

DB - Hint_info Table

힌트 테이블의 기본키 역시 serial 타입으로 정하였다.

각 단어별로 힌트를 기록해두었으며 추후 text형태의 힌트가 추가되어 우선순위에 따라 읽어올 수 있도록 hint_priority 속성을 만들어놓았다.

역시 삭제가 아닌, 사용/미사용으로 구분짓기 위해 boolean 타입으로 flag 속성을 만들었다.

힌트 테이블

- 각 단어별로 첫번째 힌트를 삽입하였다. (힌트에 관해서는 아래 자세하게 적을 예정)

 

- 추후 text 형태로 추가 힌트를 넣을 것을 고려하여 hint_priority 속성을 추가하여 힌트 순서를 관리할 수 있도록 하였다.

 

 

기본적으로 테이블 구성은 이렇게 하였고, 코드상 Spring-data-JPA 모듈을 사용해 어플리케이션과 DB를 연동시켰는데, 이 부분에 대해서는 추후 포스트로 남겨볼 예정이다.

어려웠지만 해냈다... 하지만 좀더 공부해본 후에 다시 포스팅하기로!


전체 플로우차트

전체 플로우차트

전체적인 플로우차트이다. 


Welcome

welcome

처음 Interactive canvas를 통해 앱이 실행될때마다 사용자 계정이 구글과 연동되어있는지 확인한다.

연동되어있지 않다면, 사용자에게 권한을 묻고 최종적으로 권한을 획득하면 사용자 구글 계정과 연동하게된다.

 

참고자료

https://developers.google.com/assistant/conversational/df-asdk/identity/google-sign-in#java

 

Account linking with Google Sign-In

Google Sign-In for the Assistant provides the simplest and easiest user experience to users and developers both for account linking and account creation. Your Action can request access to your user's Google profile during a conversation, including the user

developers.google.com

https://developers.google.com/assistant/conversational/df-asdk/helpers#java

 

Helper intents  |  Dialogflow and legacy Actions SDK  |  Google Developers

Explore in Dialogflow Click Continue to import our Helpers sample in Dialogflow. Then, follow the steps below to deploy and test the sample: Enter an agent name and create a new Dialogflow agent for the sample. After the agent is done importing, click Go t

developers.google.com

private GoogleIdToken.Payload getUserProfile(String idToken) {
   GoogleIdToken.Payload profile = null;
   try {
      profile = decodeIdToken(idToken);
   } catch (Exception e) {
      //LOGGER.error("error decoding idtoken");
      //LOGGER.error(e.toString());
   }
   return profile;
}

private GoogleIdToken.Payload decodeIdToken(String idTokenString)
      throws GeneralSecurityException, IOException {
   HttpTransport transport = GoogleNetHttpTransport.newTrustedTransport();
   JacksonFactory jsonFactory = JacksonFactory.getDefaultInstance();
   GoogleIdTokenVerifier verifier =
         new GoogleIdTokenVerifier.Builder(transport, jsonFactory)
               // Specify the CLIENT_ID of the app that accesses the backend:
               //.setAudience(Collections.singletonList(clientId))
               .build();
   GoogleIdToken idToken = verifier.verify(idTokenString);
   return idToken.getPayload();
}

@ForIntent("Default Welcome Intent")
public ActionResponse defaultWelcome(ActionRequest request) throws ExecutionException, InterruptedException {
	
    ResponseBuilder rb = getResponseBuilder(request);

	...

	if (request.isSignInGranted()){
   		GoogleIdToken.Payload profile = getUserProfile(request.getUser().getIdToken());
   		System.out.println("case 1");
   		//신규유저인지 아닌지
   
   		...
   
   		return ...
	}
	else{
   		System.out.println("case 2");
   		return rb.add(new SignIn()).build();
	}

}


일단 항상 case 2 로 들어간다.

계정연동이 되어있지 않다면, Helper Intents를 통해 사용자 권환 획득 후 구글계정과 연동한다.

계정연동이 되어있다면, case2에서 다시 welcome intent로 다시 들어가서 case1으로 가게 된다.

 

GoogleToken.Payload ------
getEmail :jerrygoha@gmail.com
getEmailVerified :true
getHostedDomain :null
getIssuee(use getAthoriedParty instead) :null
getUserId(use getSubject instead) :1058543...69355
IdToken.Payload ------
getAccessTokenHash :null
getAuthorizationTimeSeconds :null
getAuthorizedParty :null
getClassReference :null
getSubject :105854...69355

toString :{
“aud”:“472976746524-vhgtcrm4...ae.apps.googleusercontent.com”,
“email”:“jerrygoha@gmail.com”,
“email_verified”:true,
“exp”:159...08,
“iat”:159...408,
“iss”:“https://accounts.google.com”,
“jti”:“c4bd34102ee0...a9c8c1e705a3”,
“nbf”:159...108,
“sub”:“105854...69355",
“name”:“하제훈“,
“picture”:“https://lgoogleusercontent.com/-A06B-58R-Qk/AAAAAAAAAAI/AAAAAAAAAAA/AMZuucmKOJC...qxcBA/s96-c/photo.jpg”,
“given_name”:“제훈“,
“family_name”:“하”}

GoogleIdToken.Payload 를 사용해 계정 연동 및 정보를 얻어왔고,

GoogleIdToken.Payload profile = getUserProfile(request.getUser().getIdToken());
String email = profile.getEmail();

이런식으로 사용자 Email을 가져와 저장했다.

 

이후 얻어온 이메일 정보를 통해 DB에 새로운 사용자를 등록하거나, 이미 등록된 사용자를 찾아 데이터를 가져오게 된다.

 

(DB관련 로직쪽은 추후 따로 포스팅해볼 예정이다.)

 

사용자 데이터를 가져온 이후, 발화에 맞춰 인텐트가 흘러가게된다.

 

메인화면
DB에서 데이터를 받아와 좌상단 이메일, 경험치 그리고 우상단의 힌트, 코인 정보를 출력한다.
스테이지를 선택할 수 있는 화면. 사용자 레벨에 따라 선택할 수 있는 스테이지가 늘어난다.
난이도를 선택할 수 있는 화면. 난이도에 따라 입장료와 보상이 다르다.

 


Ingame

인게임 인텐트이다.

DB에서 스테이지별 단어를 모두 받아온 뒤, N개(정답 수) 단어를 랜덤하게 골라 게임판을 만들게 된다.

그리고 제한시간이 카운팅되고, 사용자에게 발화를 입력받는다.

입력받는 발화에 따라 정답을 체크하던가, 힌트를 사용하던지 다른 기능을 이용하게된다.

 

Ingame - 게임판 알고리즘

 

게임판

게임판

위와 같은 게임판을 만들기 위해서는 알고리즘이 필요하다.

 

  1. 게임판 안에 정답 단어가 모두 들어가있어야한다
  2. 정답 단어 외에는 다른 단어가 나와서는 안된다.
  3. 위 조건을 모두 만족하며, 단어들은 일정한 규칙성 없이 항상 무작위로 배치되어야 한다.
  4. 단어들을 적합하게 배치한 후, 나머지 칸을 2번 조건을 만족하도록 채우고 2차원 배열에 넣어 front로 보내준다.

첫글자는 어디에?

단어 배치 알고리즘 이전에, 단어의 첫번째 글자를 어디에 위치시킬지에 대해 생각해봐야 한다.

이 부분에선 두가지 조건을 생각해보았다.

 

  1. 정답이 없는 모든 칸을 한번씩 거쳐야 한다.
  2. 첫번째 글자를 놓는 방식에 특정한 규칙이 생기면 안된다.

따라서 정답을 위치시킬 수 있는 자리가 나올때까지 모듈러 연산을 통해 가로축과 세로축의 모든 칸을 지나며 첫글자를 위치시킨다.

for (int i = 랜덤포인트+1; i%row!=첫 랜덤포인트; i++) {
	for (int j = 랜덤포인트+1; j%col!=첫 랜덤포인트; j++) {
	// 내부 코드
	}
}

먼저 열의 인덱스 중 하나의 인덱스를 랜덤으로 뽑는다. 그리고 해당 열의 인덱스에서 랜덤한 행의 인덱스를 뽑는다.

행의 인덱스를 순차적으로 증가시키는데 모듈러 연산을 통해 처음 뽑은 행의 인덱스 바로 전까지 돌림으로써 모든 행을 탐색하게 된다.

해당 행을 탐색한 뒤, 열을 하나 증가시킨다.

이런식으로 증가한 열이 처음 뽑은 열의 인덱스 바로 전까지 오면 모든 게임판 탐색이 끝나게 된다.

 

 

게임판 안에는 모든 정답 단어가 들어가야한다.

첫글자 고정 이후 단어 배치는 기본적으로 완전 탐색 과정을 거친다.

첫글자를 고정시킨 후, 시계방향으로 회전을 하여 해당 위치에 단어를 놓을 수 있을지 확인한다.

 

모든 단어를 넣을수 없다면...???

탐색을 진행하는 단계에서 조건을 만족하지 못한다면 이전 단계로 돌아가는것이 아닌 실패 하도록 하였다.

따라서 탐색은 최종 성공할때까지 반복하여 진행된다.

DOG, OIL이 정답이라고 가정했을때, 나머지 칸을 어떻게 채워야 정답 단어 이외의 다른 단어는 들어가서는 안된다는 조건을 만족할까?

정답을 모두 배치한 이후에 큰 문제에 봉착했다.

 

한가지는 AOG(action on google) 상에서 사용하는 호출어가 정답 관련 발화로 들어가서 해당 Intent로 연결되는것이 아닌, 정답이 맞다 아니다 로 판단되는데 쓰일 수 있다는것이다.

이 부분은 우리가 사용하는 호출어가 정답 단어로 나올 수 없게 하고, 해당 호출어에 대해서 철저한 기계학습을 시켜 Intent가 꼬이지 않도록 해주는것으로 해결할 수 있었다.

 

다른 한가지 문제는 나머지 칸을 채워넣어야하는데, 이 과정에서 의도치 않게 다른 단어가 만들어질 수 있는것이다.

(예를 들어 위와같은 경우 O, D 가 들어가 GOD이 만들어질 수 있다.)

이와 같은 경우가 없도록 해야했는데, 꽤 어려운 문제였다.

 

바로 세상에 있는 모든 단어에 대해 (엄청나게)간단히 검증할 수 있는 방법이 없다. 는 것이다

따로 사전 API를 연동하여 한 글자를 삽입할때마다 쿼리를 날려 해당 부분 주위에서 정답 이외의 단어가 만들어지는지, 중복된 정답 단어가 또 만들어지는지 검증할 수는 있겠지만, 제한된 개발 시간 안에 해당 기능을 깔끔하게(=최소한의 딜레이로) 구현할 수 있을지가 의문이였고

가능한한 최소한의 딜레이로 구현한다고 해도, 점점 높아지는 스테이지에 따라 게임판도 커지기때문에 딜레이 부분에서 가늠하기 힘들었다.

 

그리고 만약 API를 연동하여 지속적으로 쿼리를 쏜다고 했을때, 횟수 제한이 걸릴 수 있는 등 많은 제약이 있다고 판단되어 DB에 테이블을 만든 후, 세상에 존재하는 모든 영어단어를 넣어보면 어떨까? 라는 무식한 생각도 했었다.

 

?

포기했다.

개발 기간이 좀 더 길었다면, 이 문제에 대해 더 고민해볼 수 있지 않았을까...

 

해당 문제에 관해서 구글링을 해본 결과, 여러 게임에서 정답 외의 칸에 자음만 넣어 다른 단어가 만들어질 수 있는 확률을 최대한 낮추고 있었다.

당장 떠오르는게 egg.....

자음으로만 대체한다고 했을때, 100% 다른 단어가 나오지 않는지 현재로서 검증이 되지 않았고, 특정 단어 안에 또 다른 단어가 겹치는 경우도 있었기때문에 결국 완벽하게 배제하지는 못했다.

 

이부분은 조금 더 고민해보고 기능을 개량해보고 싶다는 생각이 들어, 추후에 다시 해당 프로젝트를 손볼 기회가 생긴다면 제일 먼저 시도해볼것같다.

 

인게임 화면. 정답 단어를 찾아보자

 

Ingame - 정답 단어 표시

첫번째 프로토타입을 만들어보고 받았던 피드백 중 한가지가 맞춘 단어에 대해서 따로 표시해줬으면 좋겠다는 것이였다.

이런식으로

방법은 간단하다.

게임판이 2차원배열의 형태로 이루어져있기때문에 정답 단어의 좌표값을 저장 후, front에서 좌표를 읽어 해당하는 부분에 그래픽 효과를 넣으면 되는것이다!

이정도면 깔끔하게 표현되는듯하다.

Ingame - 힌트

힌트의 경우, 정답 단어당 3번의 힌트를 사용할 수 있도록 정하였다.

각 단어당 3번의 힌트는 규칙이 정해져있다.

 

첫번째 힌트 : 카테고리, 주제 등 포괄적으로 단어를 지칭하는....? ex) cat -> animal

두번째 힌트 : 정답 단어의 글자수.

세번째 힌트 : 게임판에서 정답 단어의 첫번째 글자를 시각적으로 보여줌.

 

첫번째는 DB에 따로 저장을 하여, 쿼리를 날려 힌트를 볼 수 있도록 하였고

두번째는 단어의 수를 구해서 시각적으로 표현,

마지막으로 세번째 힌트는 첫번째 글자를 가져와 게임판(2차원)배열을 탐색하여 같은 단어가 있는 부분을 시각적으로 변화를 줘 알려주었다.

 

그리고 힌트를 사용할때마다 DB에 update 쿼리를 날려 User table이 update되도록 하였다.

첫번째 힌트
두번째 힌트
세번째 힌트 


Result

result

단어를 시간안에 모두 맞추었을 경우, 성공 / 시간안에 맞추지 못했을경우 실패

성공했을때에는 보상을 받게 되고, 실패했을때엔 보상을 받지 못한다.

그리고 결과화면 아래에는 맞춘 단어(흰색)와 맞추지 못한 단어(붉은색)가 나오게된다

 

각 결과에 따른 경험치/코인 변동 역시 실시간으로 DB에 update 된다!

성공!

 

실패


Ranking

순서대로 잘 나온다.

랭킹은 기본적으로 User table 을 Sorting 하여 전체 순위를 나타내주고, 이때 유저가 몇번째에 위치해있는지 알려준다.

 

DB에 쿼리를 날려 List 형태로 데이터를 가져오게되는데, List형태로 가공되기 이전에 특정 규칙에 의해 정렬된다.

 

정렬 순서는 유저 레벨에 따라 정렬을 하고 레벨이 같다면 경험치, 경험치까지 같다면 계정이 만들어진 시간에 따라 정렬을 하게된다.


Setting

세팅

현재 효과음은 켜고 끌 수 있지만, 배경음은 넣어놓지 않은 상태라 따로 제어하는 로직을 넣어두지 않은 상태이다.

 

리셋은 말 그대로 사용자 데이터를 초기상태로 리셋하는것이다.

이부분에 있어서도 회의를 통해 의견 조율이 필요했는데

DB상에서 해당 유저 데이터 자체를 깔끔하게 날려서 다시 접속을 했을 때, 완전히 새로운 유저로 DB에 재등록되게 할 것인지,

기본적인 유저 정보(이메일, 만들어진 날짜, 최근접속)는 살리고 레벨, 경험치, 힌트, 코인에 한해 초기화를 할것인지에 대해서였다.

 

일단은 후자로 정해져서 DB에 해당 쿼리가 날아가도록 UserController.java, UserRepository.java, User.java 에 각 로직을 추가하였다.


Shop

상점

상점 부분은 로직 구현이 되지 않은 상태이다.

 

코인의 경우, 1:1 비율로 현금결제를 해야하고, 힌트의 경우 1개당 100코인으로 구매를 해야하는데 이 방식이 유저에게 꽤나 부담으로 작용할 것 같아 광고를 보면 재화를 얻을 수 있게 해주는 등 기획적인 부분에서의 수정이 들어갈 예정이다.

 

따라서 해당 부분은 기획적으로 틀이 잡히면 로직 구현이 들어갈것같다. 

 

https://github.com/jerrygoha/CrossWord_ActionOnGoogle

 

jerrygoha/CrossWord_ActionOnGoogle

Contribute to jerrygoha/CrossWord_ActionOnGoogle development by creating an account on GitHub.

github.com


마치며...

최대한 깔끔하게 정리해본다고 했는데 군데군데 미숙한부분들이 많은것같다.

어쨌던, 7월 한달간 개인프로젝트를 진행해보고, 이후 팀 프로젝트를 진행하면서 스스로 얻어가는것들이 많은듯하다.

 

이번 인턴을 통해 현업 프로세스를 체험해볼 수 있어 정말 좋았고, 흐지부지 한듯 안한듯 애매한 결과물이 아닌 만듦새가 좋은 결과물을 만들어내서 정말 스스로에게 잘했다고 칭찬해주고싶다.

 

하지만 동시에 부족한부분들이 정말 많았고, 피드백도 많이 받았기때문에 그 순간순간 느꼈던것들을 계속 보완할 수 있도록 노력하고, 자신감을 갖되, 자만하지 않도록 늘 조심해야겠다.

 

이후 프로젝트는 해당 repo가 아닌 다른 repo에서 관리할 예정이라 본 포스트에서는 업데이트를 할 수 없게 되었다...ㅠㅠ

 

 

반응형

댓글

💲 추천 글