지원 이유?

먼저 나는 컴퓨터공학 복수전공자인데 2020년 전기 대학 졸업을 앞두고 처음 싸피 과정을 알게 되어 지원했다가 ct문제를 준비도 없이 본 결과로 탈락했다.

그 후로 운이 좋게도 일본 회사에 취직하여 일본으로 넘어갔다가 2년 반 경력을 끝으로 다시 귀국하게 되었는데
한국에 와서 다시 취업을 하려니 경력직인데도 불구하고 서류 합격률은 대졸 신입 시절보다 낮았고
면접을 보면 볼수록 부족하다는 생각만 하게 되었으며
한 번은 연봉이 맞지 않아 못가게 된 곳이 있는가 하면...

아무튼 참담했다. 
 
와중에 인스타에 싸피 10기 광고가 떴고
2022년 9월 퇴직자인 나는 슬슬 면접에서 "공백기가 있네요?"라는 질문을 받기 시작했으므로
정리하자면 다음과 같은 이유로 싸피에 지원하게 되었다. 

- '공백기에 공부했다'를 증명할 수 있음
- 자소서, 프로젝트 전반 취업 컨설팅
- 개발자 커뮤니티 구축
 

지원

지원은 정말 쉬웠다. 요구하는 것도 학력, 자격증, 경력사항 정도였다.

하나 추가되는 게 있다면 전국에 5가지 캠퍼스가 있는 만큼 1, 2, 3순위 지망 캠퍼스와 각 캠퍼스에서의 특화 트랙을 1, 2순위로 결정해야한다는 점이다. 
 
나는 일단 경력직이고 짧아도 혼자서 웹 서비스를 운영해본 경험이 있기 때문에
아무리 공백기 증명하려고 참여하는 거래도 웹 기초 과정을 또 수강하는 건 무의미하다고 판단해서 (생각없이) 서울의 임베디드를 1순위로 선택하고 나머진 무관으로 처리했다. 

다만 SW 적성 진단을 보기 전에

- 서울 집값 어떡하지! 월세 감당 못하는데
- 코틀린 경험이 있는데 모바일을 하는 편이 낫지 않나?

란 생각에 구미 캠퍼스로 변경할 수 있겠냐 질문했으나 얄짤없이 "불가"하단 답을 받았다..

혹시 11기에 지원하는 사람이 있다면 지원서 작성할 때 캠퍼스를 잘 고르시기를..
 

에세이 작성

500자라 딱히 어려울 건 없었던 것 같다.

교육 과정인지라 취업할 때처럼 '이것도 저것도 해본 적이 있다' 보다는 '이런 걸 해보고 있는데 이런 부분에서 어려움을 느끼고 있다'의 느낌으로 작성했다. 
 

SW 적성 진단(전공자: 코딩 테스트)

백준에 [삼성 SW 역량 테스트 기출 문제]라는 문제집이 있어서 그걸 10개 정도 풀어보았고(주로 구현 문제였다)

문제집: 삼성 SW 역량 테스트 기출 문제 (baekjoon)

www.acmicpc.net


싸피 공홈에 FAQ에 나와있기를 [SW Expert Academy]에서 D1~D3을 풀어보면 된다기에 추가로 몇 문제 풀어보았다(입출력 방식에 익숙해질 겸)

SW Expert Academy

SW 프로그래밍 역량 강화에 도움이 되는 다양한 학습 컨텐츠를 확인하세요!

swexpertacademy.com

 
정작 본 시험에선 삽질을 하다 시험 종료 1분 전에 제출해서 문제를 다 맞춘건지 테스트 케이스를 통과했는지도 알 수가 없었는데 아무튼 합격은 할 수 있었다.


인터뷰

찾아보니 어떤 분은 아침 7시 반에 인터뷰를 진행했다고 하시는데
와... 내가 7시 반이었다면 역삼에 제대로 도착이나 할 수 있었을까 싶다.

아무튼 나는 운이 좋게도(?) 오후 2시로 정해졌고
20분 전에 가서 면접 대기하고 이후 안내해주시는 대로 따라가니 어느새 면접은 끝나있었다. 

준비는.. 음..

찾아보면 스터디를 만들어서 연습했다고들 하시는데 나는 그렇게까진 안했다.

다만 부모님이나 친구들과 기술 관련 얘기를 하며 터무니없는 상상을 해보는(?) 게 일상이었는데 이게 꽤 도움이 된 것 같다.

또 마침 그 전날 뉴스에서 봤던 내용과도 잘 섞을 수 있는 주제였던 덕분에 pt면접에서 "주제를 요약하는 것에 그치지 않고 내 생각"을 말할 수 있었던 것 같다.

물론 계속되는 질문에 말이 꼬이고 대답을 하다 '으, 망했군'싶기도 했지만

결과는.. 다행히 합격이었다.

 


앞으로


일단은 입과처리를 했다.

임베디드 분반 시험이 있고 거기서 떨어지면 자바나 파이썬의 웹 백엔드 과정을 수강해야하니 일단은 또 하나의 벽을 넘고 봐야겠지만
1학기만 수강하더라도 알고리즘 실력을 연마할 수 있는 좋은 시간이 될 것 같다. 열심히 해봐야지

PR 체크할 때 이런 에러 로그가 나오면서 danger가 될 땐 workflow yml을 살펴보자

로그 전체

/opt/hostedtoolcache/Ruby/2.6.10/x64/lib/ruby/gems/2.6.0/gems/danger-8.6.1/lib/danger/scm_source/git_repo.rb:139:in `find_merge_base': Cannot find a merge base between danger_base and danger_head. If you are using shallow clone/fetch, try increasing the --depth (RuntimeError)
    from /opt/hostedtoolcache/Ruby/2.6.10/x64/lib/ruby/gems/2.6.0/gems/danger-8.6.1/lib/danger/scm_source/git_repo.rb:22:in `diff_for_folder'
    from /opt/hostedtoolcache/Ruby/2.6.10/x64/lib/ruby/gems/2.6.0/gems/danger-8.6.1/lib/danger/danger_core/dangerfile.rb:274:in `setup_for_running'
    from /opt/hostedtoolcache/Ruby/2.6.10/x64/lib/ruby/gems/2.6.0/gems/danger-8.6.1/lib/danger/danger_core/dangerfile.rb:284:in `run'
    from /opt/hostedtoolcache/Ruby/2.6.10/x64/lib/ruby/gems/2.6.0/gems/danger-8.6.1/lib/danger/danger_core/executor.rb:29:in `run'
    from /opt/hostedtoolcache/Ruby/2.6.10/x64/lib/ruby/gems/2.6.0/gems/danger-8.6.1/lib/danger/commands/runner.rb:73:in `run'
    from /opt/hostedtoolcache/Ruby/2.6.10/x64/lib/ruby/gems/2.6.0/gems/claide-1.1.0/lib/claide/command.rb:334:in `run'
    from /opt/hostedtoolcache/Ruby/2.6.10/x64/lib/ruby/gems/2.6.0/gems/danger-8.6.1/bin/danger:5:in `<top (required)>'
    from /opt/hostedtoolcache/Ruby/2.6.10/x64/bin/danger:23:in `load'
    from /opt/hostedtoolcache/Ruby/2.6.10/x64/bin/danger:23:in `<main>'
Error: The process '/opt/hostedtoolcache/Ruby/2.6.10/x64/bin/danger' failed with exit code 1

원인

로그 두 번째 줄에 있는

 

If you are using shallow clone/fetch, try increasing the --depth (RuntimeError)

 

로 알 수 있는데, danger가 체크용으로 해당 브런치를 clone하려는데 shallow clone을 사용하고 있어 가장 마지막의 1 커밋만 가져와 이력을 전부 확인할 수 없는 것이 원인인 듯싶다.

shallow clone?

일반적인 git clone 은 tag, commit 을 모두 checkout 하지만 시간이 더 걸리기 때문에, 모든 커밋을 가져올 필요가 없는 경우 shallow clone 을 사용해 checkout 시의 볼륨의 줄일 수 있다고 한다.

해결

https://github.com/actions/checkout 에서 checkout workflow의 사양을 확인해보면 depth의 기본값은 1이다.
찾아보니 수를 늘리는 것보단 오히려 아예 커밋을 가져오지 않도록 하는 게 방법인 것 같아서 일단은 그렇게 해결.

  danger:
    timeout-minutes: 5
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@master
        with:
          fetch-depth: 0

브런치명에 `{grep-value}`를 갖는 모든 git branch를 삭제

$ git branch | grep "{grep-value}" | xargs git branch -D

 

불과 얼마전에 @JsonCreate/ @JsonProperty("") 를 사용해서 파스칼 -> 카멜식으로 역직렬화하는데 성공했다고 일기를 썼는데, 오늘 더 좋은 방법을 찾았다. 

PropertyNamingStrategy

명명 규칙의 차이 정도라면 이 클래스를 활용해서 쉽게 변경 가능하다는 듯하다. 

ObjectMapper 를 인스턴스화 할 때, 값이 없는 필드가 있어도 오류를 내지 않고 넘어가도록 `FAIL_ON_UNKNOWN_PROPERTIES`를 지정했던 것과 비슷한데

ObjectMapper objectMapper = new ObjectMapper()
                .setPropertyNamingStrategy(PropertyNamingStrategy.UPPER_CAMEL_CASE)
                .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

이렇게 하면 된다. @JsonCreator를 단 생성자는 지워도 무관하고, 따로 설정을 추가하지 않아도 알아서 필드명과 동일하게 나온다. 

 

Jackson CASE 정리

CAMEL_CASE thisIsExample
UPPER_CAMEL_CASE (PASCAL_CASE) ThisIsExample
SNAKE_CASE this_is_example
KEBAB_CASE this-is-example
UPPER_CASE THIS_IS_EXAMPLE
LOWER_CASE thisisexample

 

Jsoup

지금까지 웹사이트에서 필요한 정보를 찾아내기 위해서는 python의 beautifulSoup를 써왔는데, 자바로 개발하고 있는 프로젝트에 python 파일을 따로 던져주기도 애매해서 찾아봤더니 자바에도 있었다. Jsoup! 

 

사용 준비

  1. https://mvnrepository.com/artifact/org.jsoup/jsoup
  2. dol-maplestory는 gradle project이므로 아래 내용을 build.gradle에 추가하기만 하면 사용 가능하다
// https://mvnrepository.com/artifact/org.jsoup/jsoup
implementation 'org.jsoup:jsoup:1.15.1'

 

JsoupUtil.class

자, 그럼 말도 안 되게 간단한 준비를 마쳤으니 빠르게 유틸 클래스를 작성해보자. 우선 [캐릭터 이름]으로 메이플 공식 홈페이지에서 랭킹 검색, jsoup parsing기능을 통해 TCharacter에 필요한 데이터를 뽑아올 것이다.

 

그 전에 메이플 홈페이지를 뒤적거리다가 알아낸 것 몇 가지

  1. 전체 월드/ 월드별 랭킹의 검색 URL은 같지만, 파라미터가 다르다.
  2. 월드명은 `w={worldCode}`로 
  3. 캐릭터명은 `c={characterName}`으로 전달한다.

= worldCode값을 저장해놔야한다

 

그래서 만들었다. WorldInfo enum. 값은 월드 하나씩 다 눌러서 알아냈다. 예전처럼 월드가 많지 않아서 다행이었다.

 

그럼 이제 정말로 Jsoup를 이용해서 캐릭터 정보를 긁어보자! 

전체 랭킹과 월드별 랭킹에서 보여주는 캐릭터 정보는 같고, 랭킹값의 기준만 달라지는 것이기 때문에 로직은 이렇게 만들었다. 

JSoupUtil.class코드

  1. 캐릭터 이름을 받아서 
  2. 전체 랭킹 검색/파싱 후 TCharacter entity에 맞춰서 값을 저장한다
  3. 전체 랭킹 검색에 성공해서, entity에 값을 저장하는 것까지 성공했다면
  4. 월드별 랭킹 검색을 진행, 월드별 랭킹값을 해당 entity에 추가 저장한다

 

td태그에 클래스도 아이디도 지정되어있지 않아서, 메이플이 랭킹 페이지의 html구조를 바꿔버리면 쓸 수가 없는 코드이지만...

 

이 코드를 짜는 도중에 하나 더 깨달은 게 있는데, 바로 어제 만든 API(/v1/maplestory/representative-character/:accountId)를 사용하기 위해선 개인 정보인 accountID가 반드시 필요한데, 그럼에도 대표캐릭터 관련 정보만 가져올 수 있는 데 반해 이번에 만드는 건 캐릭터 이름만으로도 충분하다는 거였다. 내가 내 블로그에 글 올릴 때도 accoundID를 가렸는데, 누가 개인이 만든 신뢰 못할 사이트에 개인 정보를 넣을까... 싶어졌기 때문에, TUser table에 정의한 accountId 필드는 삭제..ㅎ 만든 API는 soap api공부한 셈 치고 냅두기로 했다.


API정리

api는 이미 서비스중인 '메이플지지'라는 사이트에서 확인할 수 있는 '메애기 한눈에 보기'와 유사하다. 순서도 똑같을 정도로 말이다. 

  1. 메이플스토리 공식 홈페이지에 로그인
  2. 대표 캐릭터 변경 페이지에 접속
  3. 월드/캐릭터 선택 영역의 우측 서버 선택
  4. 대표 캐릭터 변경 페이지를 전체선택/복사
  5. 복사한 내용 그대로 request body에 실어 보내기

여기서 메이플지지에서는 뭔가 프론트 로직...? 을 통해서 캐릭터 이름을 골라내는 듯한데, 살짝 오류가 있어보인다. 메이플이 html구조를 바꿨는데 메이플지지 쪽에서 아직 대응을 안 한 걸수도 있지만. 어쨌든, 홈페이지에서 복사한 내용을 그대로 텍스트 에디터에 붙여넣기하면 이렇게 되는데

NEXON
메뉴
메이플스토리
OFF
15
로그아웃
helloproud25
주의

본문 바로가기
주 메뉴 바로가기

뉴스가이드랭킹커뮤니티미디어고객지원
마이메이플백에리부트해적마이메이플내가 쓴 글
내정보 관리아이템 이용내역테스트월드회원탈퇴
내정보 관리대표캐릭터 변경메이플스토리 홈페이지 이용 시 사용할 대표캐릭터를 설정해주세요.01메이플ID 선택
helloproud
02월드/캐릭터 선택리부트
리부트
진동온돌리부트진동온돌룬지속더하기리부트룬지속더하기평균의법칙리부트평균의법칙온돌인데리부트온돌인데백에리부트백에온돌링리부트온돌링
대표캐릭터는 10레벨 이상이어야 지정할 수 있습니다.대표 캐릭터 저장
마이메이플 메인
내정보 관리
대표캐릭터 변경
비활성ID 전환/해제
제한 내역
캐릭터정보 공개설정
Family Site회사소개채용안내이용약관게임이용등급안내개인정보처리방침청소년보호정책운영정책넥슨PC방사이트맵(주)넥슨코리아 대표이사 이정헌 경기도 성남시 분당구판교로 256번길 7 전화: 1588-7701 팩스:0502-258-7322
E-mail:contact-us@nexon.co.kr 사업자등록번호 : 220-87-17483호 통신판매업 신고번호 : 제2013-경기성남-1659호 사업자정보확인ⓒ NEXON Korea Corporation All Rights Reserved.TOP

 

여기서 나의 캐릭터 이름만 쏙쏙 골라내기 위해선, 우선 중간쯤에 있는 마이메이플, [대표캐릭터 이름 + 월드명]직업명, 마이메이플, 내가쓴 글로 이루어진 곳에서 알 수 있는 [대표캐릭터명 + 월드명]을 통해 진동온돌리부트진동온돌룬지속더하기~~~ 로 된... 줄을 뽑아잡아야한다. (아무리 일기라도 그렇지 이렇게 막 써서야 나중에 기억도 못할 거 같아서 약간 두려워졌다)

 

아무튼.. 정리해보면

Controller.class코드

  1. 마이페이지 [대표캐릭터 + 월드명] 마이페이지 로 이루어진 줄에서 [대표캐릭터 + 월드명]을 알아낸다
  2. 그 다음 줄부터 [대표캐릭터 + 월드명 + 대표캐릭터] 를 포함한 줄을 찾는다
  3. 그 줄은 [캐릭터명 + 월드명 + 캐릭터명] 이 한세트로 이루어져 있다
  4. [캐릭터명]을 추출한다

API 정리

method get
endpoint /v1/maplestory/character/list
request NEXON
메뉴
메이플스토리
OFF
15
로그아웃
helloproud25
주의

본문 바로가기
주 메뉴 바로가기

뉴스가이드랭킹커뮤니티미디어고객지원
마이메이플백에리부트해적마이메이플내가 쓴 글
내정보 관리아이템 이용내역테스트월드회원탈퇴
내정보 관리대표캐릭터 변경메이플스토리 홈페이지 이용 시 사용할 대표캐릭터를 설정해주세요.01메이플ID 선택
helloproud
02월드/캐릭터 선택리부트
리부트
진동온돌리부트진동온돌룬지속더하기리부트룬지속더하기평균의법칙리부트평균의법칙온돌인데리부트온돌인데백에리부트백에온돌링리부트온돌링
대표캐릭터는 10레벨 이상이어야 지정할 수 있습니다.대표 캐릭터 저장
마이메이플 메인
내정보 관리
대표캐릭터 변경
비활성ID 전환/해제
제한 내역
캐릭터정보 공개설정
Family Site회사소개채용안내이용약관게임이용등급안내개인정보처리방침청소년보호정책운영정책넥슨PC방사이트맵(주)넥슨코리아 대표이사 이정헌 경기도 성남시 분당구판교로 256번길 7 전화: 1588-7701 팩스:0502-258-7322
E-mail:contact-us@nexon.co.kr 사업자등록번호 : 220-87-17483호 통신판매업 신고번호 : 제2013-경기성남-1659호 사업자정보확인ⓒ NEXON Korea Corporation All Rights Reserved.TOP
response {
    "List": [
        {
            "AvatarImgURL": "https://avatar.maplestory.nexon.com/Character/180/AIDKJCJCKABDGBEFLNHNIBOJAMNBLPHNIMHMCFAKOHEFDBKBGIFAKJEHGIHHMIJDCAGCIDNGMDBDDELLPGEICCKAEHEAMINDOAKDGBHDPKCJPEJIIGAECLBLAJGPFMBLJDEAOAIDCAAAAKAAEIDDCFJHINPCBHLLGBDPHMLECLIMOGIGFEDNIBEBGHLGKPPFLNKKDHEFMLMJGKAHCOEPBKEAMNIIFPGBEAMPIFHEBPPKHENJLAOOANNGCMPOGINA.png",
            "WorldName": "리부트",
            "CharacterName": "진동온돌",
            "Lev": 246,
            "Exp": 294751868547,
            "Pop": 12,
            "TotRank": 36011,
            "WorldRank": 29264,
            "jobId": 29,
            "guild": "퍼런"
        },
        {
            "AvatarImgURL": "https://avatar.maplestory.nexon.com/Character/180/JLKMFLHMIKKFKDNEBAEPJNDAPLIAKMJKMGADBMEPFBEDKABENHNFHPOMMONCODHCLGNMCBJMOJJACLJIAHNCNOGAMDNKAHCAHKMINPCFMDLCBJIOHNFGIEHCKBHHBEIBLMGGOGNGIOIHOGLGAPGHMGKONOPGGPEPFHOKCFNJKELIKEIINOGGGIBJCPCCAEODEOCKJBACNBGHBBDILIIFKFLMKLNPGHOMFMCKLOKIHDPILGINHOGJCIIGGDHMENDN.png",
            "WorldName": "리부트",
            "CharacterName": "룬지속더하기",
            "Lev": 140,
            "Exp": 16020880,
            "Pop": 0,
            "TotRank": 2078640,
            "WorldRank": 1699149,
            "jobId": 301,
            "guild": ""
        },
        {
            "AvatarImgURL": "https://avatar.maplestory.nexon.com/Character/180/MJHELABNCPFJLHEGCHHOOEOKJIFCPKGFPNLJJCOEFALGOILHLLICAGGBAMNEHPGAEIEAJJAGKEPMNHENHGCNINHBNDGNDGCJGAMAOOEFFKLNNMMFNBMIBJPEKOGCAAFPEDLPJMMLKPGHDMDCCBPLHKGMLLLCFADEJPHGOBGLNHDDJCIBFHEJLIAGFLEAFHMCEBNMAHJOAABNDAFOLDIEIFAENMAMEGOKDMFCJIHGGCPALKGDMDMFALEBMOBILDPP.png",
            "WorldName": "리부트",
            "CharacterName": "온돌인데",
            "Lev": 140,
            "Exp": 652093,
            "Pop": 0,
            "TotRank": 2383258,
            "WorldRank": 1951045,
            "jobId": 600,
            "guild": ""
        },
        {
            "AvatarImgURL": "https://avatar.maplestory.nexon.com/Character/180/JBCLMLOOKMCJBKIAMAFMOHKCKBDDKCJHIEKJNODEPPPNENBNPHPOMHEDINLAIABGDBMAHDMKGGEMAAMKIGAMMENBDDPINMCMCGJAMDHKLCAMDDBGJJMBNAGGNNCEGOHHKDBKLLKJNDCGBAOAJCFJHMCMPLLOKLEKOJDANPEDFKHLMBJMIBIHKGAEOIFMANPMNAMHBDKJLLOBGACHBMKONMGEGKCCNHPBCEPEBEAJJMIKHMJOPAAAMNEFCJBPLBNE.png",
            "WorldName": "리부트",
            "CharacterName": "백에",
            "Lev": 246,
            "Exp": 141620834,
            "Pop": 7,
            "TotRank": 37358,
            "WorldRank": 30385,
            "jobId": 46,
            "guild": "파란"
        }
    ]
}

 

다음 할 일

  1. 일단 캐릭터 정보를 저장해두고...(실장을 위해)
  2. 유니온 관련 정보 계산해서 보여주는 API(등급, 레벨, 키우지 않은 직업, 중복으로 키운 직업 등)
  3. 캐릭터별 메할일 설정 API

사용할 메이플스토리api(GetCharacterInfoByAccountID) 정보

http://api.maplestory.nexon.com/soap/maplestory.asmx?op=GetCharacterInfoByAccountID 

 

MapleStory 웹 서비스

MapleStory 전체 작업 목록을 보려면 여기를 클릭하십시오. GetCharacterInfoByAccountID 테스트 테스트 폼은 로컬 컴퓨터의 요청에만 사용할 수 있습니다. SOAP 1.1 다음은 샘플 SOAP 1.1 요청 및 응답입니다.

api.maplestory.nexon.com

 

Request

링크에 들어가보면, 해당 API를 사용하기 위해서는 `Content-Length`와 `AccountID`값이 필요하다는 걸 알 수 있다. 

`Content-Length`는 SOAP API 요청할 때 쓰는 xml data의 실제 byte 길이,

`AccountID`는 메이플스토리 공식 홈페이지에서 확인 가능한 값이다. 

 

AccountID 확인 방법

내가 알아낸 방법은 두 가지가 있다. 

 

첫 번째: 마이메이플(내정보 관리)에서 확인하는 방법

  1. 메이플스토리 공식 홈페이지에 접속, 로그인
  2. 마이메이플(내정보 관리)에 접속
  3. 대표캐릭터 표시 영역 밑쪽에 [캐릭터 정보 더보기>] 클릭
  4. 캐릭터정보 페이지에서 마우스 우클릭 -> 페이지 소스 코드 보기
  5. `char_custom_sel` 로 페이지 내에서 검색
  6. <option value="">메이플ID</option> 아래 쪽에 하나 더 있는 option의 value가 accountID

 

두 번째: 대표캐릭터 변경 페이지에서 확인하는 방법

  1. 메이플스토리 공식 홈페이지에 접속, 로그인
  2. 대표캐릭터 변경 페이지에 접속
  3. 마우스 우클릭 -> 페이지 소스 코드 표시
  4. 페이지 내에서 `sel_mail_id`로 검색
  5. javascript코드 말고 HTML 태그로 쓰인 곳을 찾았다면 그곳의 value가 accountID

 

Response

요청값을 알았다면 이제 반환값을 알아볼 차례이다. soap api이기 때문에 xml형식으로 반환된다. postman으로 요청 후 돌아온 값을 보자면...

<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <soap:Body>
        <GetCharacterInfoByAccountIDResponse xmlns="https://api.maplestory.nexon.com/soap/">
            <GetCharacterInfoByAccountIDResult>
                <xs:schema id="NewDataSet" xmlns="" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
                    <xs:element name="NewDataSet" msdata:IsDataSet="true" msdata:UseCurrentLocale="true">
                        <xs:complexType>
                            <xs:choice minOccurs="0" maxOccurs="unbounded">
                                <xs:element name="UserInfo">
                                    <xs:complexType>
                                        <xs:sequence>
                                            <xs:element name="AvatarImgURL" type="xs:string" minOccurs="0" />
                                            <xs:element name="WorldName" type="xs:string" minOccurs="0" />
                                            <xs:element name="CharacterName" type="xs:string" minOccurs="0" />
                                            <xs:element name="Lev" type="xs:short" minOccurs="0" />
                                            <xs:element name="Exp" type="xs:long" minOccurs="0" />
                                            <xs:element name="Job" type="xs:string" minOccurs="0" />
                                            <xs:element name="JobDetail" type="xs:string" minOccurs="0" />
                                            <xs:element name="Pop" type="xs:int" minOccurs="0" />
                                            <xs:element name="TotRank" type="xs:int" minOccurs="0" />
                                            <xs:element name="WorldRank" type="xs:int" minOccurs="0" />
                                        </xs:sequence>
                                    </xs:complexType>
                                </xs:element>
                            </xs:choice>
                        </xs:complexType>
                    </xs:element>
                </xs:schema>
                <diffgr:diffgram xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" xmlns:diffgr="urn:schemas-microsoft-com:xml-diffgram-v1">
                    <NewDataSet xmlns="">
                        <UserInfo diffgr:id="UserInfo1" msdata:rowOrder="0" diffgr:hasChanges="inserted">
                            <AvatarImgURL>http://avatar.maplestory.nexon.com/Character/JBCLMLOOKMCJBKIAMAFMOHKCKBDDKCJHIEKJNODEPPPNENBNPHPOMHEDINLAIABGDBMAHDMKGGEMAAMKIGAMMENBDDPINMCMCGJAMDHKLCAMDDBGJJMBNAGGNNCEGOHHKDBKLLKJNDCGBAOAJCFJHMCMPLLOKLEKOJDANPEDFKHLMBJMIBIHKGAEOIFMANPMNAMHBDKJLLOBGACHBMKONMGEGKCCNHPBCEPEBEAJJMIKHMJOPAAAMNEFCJBPLBNE.png</AvatarImgURL>
                            <WorldName>리부트</WorldName>
                            <CharacterName>백에</CharacterName>
                            <Lev>245</Lev>
                            <Exp>294024963660</Exp>
                            <Job>해적</Job>
                            <JobDetail>바이퍼</JobDetail>
                            <Pop>6</Pop>
                            <TotRank>38309</TotRank>
                            <WorldRank>31186</WorldRank>
                        </UserInfo>
                    </NewDataSet>
                </diffgr:diffgram>
            </GetCharacterInfoByAccountIDResult>
        </GetCharacterInfoByAccountIDResponse>
    </soap:Body>
</soap:Envelope>

 

이렇게 되어있기 때문에, 내가 필요한 값(value)의 키값(key)이 다음과 같음을 알 수 있다. 

  • AvatarImgURL
  • WorldName
  • CharacterName
  • Lev
  • Exp
  • Job
  • JobDetail
  • Pop
  • TotRank
  • WorldRank

자체 API 만들기

method endpoint request response
get /v1/maplestory/representative-character/:accountId -

대표캐릭터 정보를 xml로 전달하는 메이플스토리 API에 직접 요청하지 않고 중간에 끼워넣어 json으로 받고싶을 때 쓸 수 있다.

`t_character` 테이블 정의를 할 때 굳이 Job, JobDetail을 넣어야하나... 싶어서 직업은 JobInfo enum으로 뺐기 때문에, 여러 라이브러리의 힘을 빌려서 이런 느낌으로 만들어봤다. 

참고로 dol-maplestory은 `t_character`의 값을 json 객체로 직렬화해서 반환한다. 

 

`t_character`의 구조는 이렇게 되어있는데, avatar_img_url, character_name, exp, lev, pop, tot_rank, world_name, world_rank는 xml response의 key값을 파스칼에서 스네이크 표기법으로 바꾼 것이다. 

mysql> show columns from t_character;
+----------------+--------------+------+-----+---------+----------------+
| Field          | Type         | Null | Key | Default | Extra          |
+----------------+--------------+------+-----+---------+----------------+
| id             | bigint(20)   | NO   | PRI | NULL    | auto_increment |
| avatar_img_url | varchar(255) | YES  |     | NULL    |                |
| character_name | varchar(255) | NO   |     | NULL    |                |
| clearable_boss | varchar(255) | YES  |     | NULL    |                |
| exp            | bigint(20)   | YES  |     | NULL    |                |
| job_id         | int(11)      | YES  |     | NULL    |                |
| lev            | int(11)      | YES  |     | NULL    |                |
| pop            | int(11)      | YES  |     | NULL    |                |
| tot_rank       | int(11)      | YES  |     | NULL    |                |
| world_name     | varchar(255) | YES  |     | NULL    |                |
| world_rank     | int(11)      | YES  |     | NULL    |                |
| user_id        | bigint(20)   | YES  | MUL | NULL    |                |
+----------------+--------------+------+-----+---------+----------------+

다른 게 있다면 3개 정도

  1. id
    • 캐릭터 고유 아이디. 넥슨쪽 캐릭터 아이디를 쓰는 것도 좋겠지만, 어떻게 찾아낼지 그것도 고민이고 구분만 할 수 있으면 됐기 때문에 그냥 따로 정의했다. 
  2. clearable_boss
    • 캐릭터가 깰 수 있는 보스를 알 수 있는 필드
    • 1_1/2_1/10_6 처럼 저장될 예정인데, 아래와 같이 해석할 수 있다
      • boss id 1에 대해 1명 입장해서 클리어
      • boss id 2에 대해 1명 입장해서 클리어
      • boss id 10에 대해 6명 입장해서 클리어
    • boss id는 BossInfo enum을 참고
  3. user_id
    • 그래도 뭘 만들려면 회원가입은 있어야하지 않겠나... 싶어서 만든 `t_user` 테이블의 primary key
    • nexon maplestory accountID랑은 다른 값이다
    • 메이플스토리는 거의 필수로 유저 한 명이 캐릭터 45개쯤은 키워야하는 게임이기에 캐릭터마다의 할일을 유저에게 보여줄 수 있으면 좋겠다 싶어서 추가했다

 

알게된 것

1.
@JsonProperty("name")
을 사용해서 직렬화/역직렬화시의 키값을 지정할 수 있다

2.
@JsonCreator
    public TCharacter(
            @JsonProperty("AvatarImgURL") String avatarImgUrl,
            @JsonProperty("WorldName") String worldName,
            @JsonProperty("CharacterName") String characterName,
            @JsonProperty("Lev") Integer lev,
            @JsonProperty("Exp") Long exp,
            @JsonProperty("JobDetail") String jobDetail,
            @JsonProperty("Pop") Integer pop,
            @JsonProperty("TotRank") Integer totRank,
            @JsonProperty("WorldRank") Integer worldRank
    ){
        this.avatarImgUrl = avatarImgUrl;
        this.worldName = worldName;
        this.characterName = characterName;
        this.lev = lev;
        this.exp = exp;
        this.jobId = JobInfo.getJobInfoByJobDetail(jobDetail).getId();
        this.pop = pop;
        this.totRank = totRank;
        this.worldRank = worldRank;
    }
생성자 위에 붙여 역직렬화할 때 사용할 수 있다. 

3.
@JsonRootName("t_character")
는 Json객체의 주체를 알 수 있도록 할 때 사용한다. entity class위에 정의.
사용하기 위해선 application.propertes에 이하의 설정을 추가할 필요가 있다. 
spring.jackson.serialization.wrap-root-value=true

4.
ObjectMapper objectMapper = new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
ObjectMapper를 인스턴스화할 때 `configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)`
를 추가함으로 Mapper class에 없는 properties가 있을 경우, 에러를 발생시키지 않고 스킵할 수 있다.

 


다음 할 일

Jsoup를 사용한 웹페이지 크롤링.. 이라기보다 캐릭터 정보 긁어내기

 

'memo' 카테고리의 다른 글

ssafy 10기 합격  (2) 2023.06.24
Cannot find a merge base between danger_base and danger_head  (0) 2022.08.18
git local branch 일괄 삭제  (0) 2022.08.18
Json PropertyNamingStrategy  (0) 2022.06.10
Jsoup를 이용한 캐릭터 정보 탐색  (0) 2022.06.09

+ Recent posts