4개월이나 지나서야 작성하는 UMC 6th 데모데이 후기 글이다. 😅 데모데이 후기만 적기엔 아쉬워서 UMC 6th ERICA 지원부터 활동 과정, 프로젝트 시 주의사항도 함께 적어보려고 한다.
UMC 6th ERICA 지원 및 활동 과정
지원
이번 연도 1~2월에 HTML & CSS & JS 찍먹도 해보고, 창업 부트캠프도 다녀오고 이것저것 해보다가 Java 백엔드 개발자로 진로를 결정했다. 전공에서 웹 개발 관련으로 배운 게 그닥 없어서 2월엔 Spring Boot 강의를 듣고, Java 기초도 다시 정리하면서 공부했던 것 같다. 3월엔 연합 IT 동아리에 들어가려고 멋쟁이사자처럼과 UMC 중에 고민하고 있었는데, 멋사는 백엔드가 Django고, UMC는 Spring Boot & Node.js라 UMC 지원 공고가 올라올 때까지 기다리다가 바로 지원했다.
1. 코딩 경험 유무 & 배워본 언어
* 배워보았던 언어와 그 수준을 스스로 상/중/하로 평가하여 나열해주세요!
* 특히, 지원하는 파트의 기초 지식이 어느 정도인지를 포함하여 작성해 주세요.
- 안드로이드:Kotlin (Java)
- Web: HTML/CSS + JavaScript
- Spring Boot: Java / Node.js: JavaScript
(면접 시 해당 답변을 참고하여 관련 지식을 평가할 수 있습니다.)
'python 하, Java 하, C++ 중'이라고 작성했다.
2. UMC 지원 동기와 UMC 활동을 통해 기대하는 바를 알려주세요! (500자 내외)
아산 두어스 창업 부트캠프 1기 지원서랑 비슷하게 작성했다. 요약해 보면 SW창업캡스톤디자인 수업을 들으면서 전공 수업에선 크게 느낄 수 없었던 재미를 느꼈고 웹 개발 관련 진로를 찾아보게 됐다고 작성했다. Java 백엔드를 선택한 이유와 내가 UMC에 참여해서 얻을 수 있다고 생각하는 것들도 작성했다.
3. UMC에 임하는 나의 각오를 알려주세요! (500자 내외)
능동적으로, 스스로 학습하고, 팀의 문제를 함께 해결하기 위해 노력하고, 전공 수업에도 집중하고, 소통하고 도전하고 몰입하는 개발자가 되겠다고 써놨다. 🤔
4. 진행했던 프로젝트 경험을 서류로 제출해 주세요!
- 중요한 문항입니다. 협업하면서 어려웠던 점, 극복한 방법, 협업을 어떻게 진행하였는지, 해당 프로젝트를 진행한 이유, 프로젝트 내 나의 파트 등 자세하게 서술해 주세요! 협업을 통해 앱 런칭을 이루기 때문에 소통, 적극성 등 다양한 역량이 중요합니다.
- 하나의 PDF 파일로 병합해 제출하는 당신은 센스쟁이! 가능하시다면 하나의 PDF 파일로 병합 및 압축(!GB 이하로) 부탁드립니다.
- 폼으로 제출이 불가능하여 부득이하게 메일로 제출하는 경우를 위해 필수 조건을 해지해두었지만 미제출 시 감점 사항입니다. 반드시 제출 바랍니다.
'Java MVC를 활용한 숫자 야구 애플리케이션 개발'과 '외국인 유학생의 한국 정착을 위한 커리어 빌딩 플랫폼 기획' 2개를 형식에 맞춰 작성했다.
면접
면접은 다른 블로그 글을 참고하면서 준비했다. 질문이 기술적이진 않고 간단하다고 해서 몇 개만 준비해 뒀었다.
자기소개 및 백엔드 파트에 지원한 이유 / 시간을 충분히 쓸 수 있는지 /만들고 싶은 웹 또는 앱/Java 기본 개념/ 하고 싶은 말
원하는 시간을 골라 줌으로 진행했는데, 위에 적어둔 것들 중 Java 기본 개념 대신 Spring 및 웹 개발과 관련된 질문을 하셨다. 자기소개를 할 때 Spring 강의를 보며 공부 중이라고 말해서 Spring과 관련된 질문을 주신 것 같기도 하다.
- HTTP Method: GET과 POST의 차이
- REST API의 정의
- Spring 개념 어디까지 배웠는지
- DB 구축해 봤는지
- 애노테이션이 무엇인지
- DI가 무엇인지
UMC 7th 때는 6th 질문에 자바 기본 개념까지 추가해서 면접을 진행했다고 한다.
스터디
2023년에 UMC 서버 파트장을 맡으신 똘이님과 섀넌님이 Server 워크북을 같이 제작하셨고, 그 워크북으로 4월부터 6월까지 스터디를 진행했다.
UMC 6th ERICA 서버(Spring) 파트장인 마커스님이 A팀과 B팀을 둘 다 맡아 스터디를 이끌어주셨다. 마커스님 덕분에 스터디에서부터 GitHub을 기초부터 쭉 배울 수 있어서 프로젝트에서도 막히지 않고 활동할 수 있었던 것 같다.
- 아래 UMC-6th-ERICA-Server-Spring GitHub 리포지토리를 보면 Commit 컨벤션부터 PR 컨벤션까지 작성해 두신 걸 확인할 수 있다. PR도 최소 2명이 승인해야 merge할 수 있도록 해두셔서 스터디를 진행하는 동안 협업을 살짝 맛볼 수 있어서 더 좋았다.
워크북 내용을 하나하나 따라가다 보면 모두가 초보인 해커톤에서 뭐라도 할 수 있는 사람이 될 수 있다. 물론 12주 차까지 완벽하게 이해하고 똑같이 구현해봐야 한다.
- 11월에 진행한 7th Neordinary Hackathon에서 똘이님(서버 워크북 제작자)이 심사위원으로 계셔서 조금 대화할 기회가 있었는데, 워크북에서 1~3주 차 내용이 제일 중요하다고 말씀하셨다.
- 스터디가 4월부터 시작해서 9주 차부터는 종강 이후에 각자 공부했는데 이게 좀 아쉬웠다. 9주 차부터 애매하게 공부해 버려서, 7월에 참여한 해커톤에선 전날 간신히 따라 해 본 [무중단 CI/CD 파이프라인 구축]을 맡게 되기도 했다.
일반 & 소셜 로그인은 배우지 않아서 아쉽다고 생각했는데, UMC 7th부턴 워크북이 변경돼서 소셜 로그인과 Spring Data JPA의 쿼리 메서드나 @Query로 직접 SQL을 써보는 것도 같이 배운다고 한다.
프로젝트: 하루치(HARUCHI)
7월 12일부터 8월 21일까지 팀 프로젝트 하루치(HARUCHI)에 참여했다.
백엔드 개발
백엔드 개발은 7월 13일부터 8월 21일까지 진행됐다. 프로젝트에 대한 자세한 내용은 GitHub 리포지토리의 README를 참고하면 된다.
- 프로젝트 기초 개발: 24/07/13 ~ 24/07/18
- API 구현: 24/07/18 ~ 24/08/21
UMC 6th 스터디에서 맛만 봤던 Issue와 PR도 컨벤션부터 템플릿까지 만들고, 서로 PR에 리뷰해 주면서 프로젝트를 진행했다. 스터디 이후 처음으로 제대로 협업하면서 프로젝트를 진행해 봤는데 정말 만족스러웠다.
7월 18일 백엔드 회의에서 회원 관련 API를 맡게 됐을 때 솔직히 자신이 없었다. 홈페이지에서 흔히 볼 수 있는 이메일 인증 회원가입부터 로그인에 로그아웃, 회원탈퇴까지 구현해 볼 수 있다는 생각에 설레기도 했지만, Redis나 JWT에 Spring Security라는 처음 보는 프레임워크를 학습한 뒤 구현해야 해서 걱정을 많이 했다. 백엔드 API 전체 개발은 8월 4일을 목표로 해뒀었는데, 그렇게 되면 프론트엔드 쪽에서 너무 바빠진다고 하셔서 최대한 빨리 구현하려고 노력해야 했다. API마다 기한을 여유롭게 잡아 놓고 시작했는데, 몰입해서 구현하다 보니 거의 하루에서 길어도 4일 안에 구현한 것 같다.
- 이메일 인증 기반 회원가입 API (예상 7월 24일): 7월 18일 ~ 7월 19일
- JWT 일반 로그인 API (예상 7월 24일): 7월 19일 ~ 7월 22일
- JWT 일반 로그아웃, 회원탈퇴 API (예상 7월 24일): 7월 23일
- 회원 정보 조회 API (예상 7월 30일): 7월 29일
사실 JWT 일반 로그인 API을 구현할 때, Spring Security 공식 문서는 볼 생각도 못 하고 블로그랑 GitHub 리포지토리를 20개 넘게 찾아보면서 구현했다. 이것저것 잘라 붙이다 보니 코드가 엄청 복잡해져서 노트북 보면서 멍 때리다가 작은 것부터 해보자는 생각이 들어서 Refresh Token 없이 Access Token 발급 기능만 목표로 구현하기로 했다. 그렇게 과정을 쪼개고 쪼개서 쭉 연결해서 구현하다 보니, 하나의 PR에 Commit이 19번 정도 들어갔다.
- JWT Authentication Filter, SecurityConfig 관련 코드 구현
- Access Token 발급 구현
- Refresh Token 발급 구현 -> 토큰 재발급 기능 구현
- 로그아웃, 회원탈퇴 구현
이후엔, 원래 발급한 JWT를 엔티티에 저장하도록 설계했었는데 Redis까지 욕심이 생겨서 Refresh Token을 Redis에 저장하도록 수정했다.
그러고 나서 8월쯤엔 자잘한 오류들을 고치고, 프론트엔드와 협업하기 위한 문서 작업도 진행했다.
프론트엔드 & PM과의 협업
a. REFACTOR
처음엔 로그인 방식을 소셜 로그인으로 정했었는데, 문제가 생겨서 일반 로그인(이메일, 비밀번호)으로 변경됐다. 구체적으로는 JWT를 활용해 Access Token과 Refresh Token을 발급받도록 구현하기로 했다. 7월 말쯤 내가 맡은 기능 구현을 다 끝냈는데 8월 초에 디스코드 방이 엄청 활성화됐다. 중점은 [토큰에 만료 시간이 꼭 필요한가]였다.
- 백엔드 입장
- 처음부터 이렇게(Access Token & Refresh Token 함께 사용) 하기로 했었고 구현까지 끝난 상태다.
- 만료 시간을 없애면 보안상의 문제가 생길 수밖에 없다.
- 토큰 만료 시간으로 30일 미접속 시 로그아웃되도록 구현해 둔 상태다.
- 프론트엔드 입장
- 유효 시간에 따라 토큰을 다시 요청하는 로직을 추가하는 게 시간이 좀 걸릴 것 같다.
- 보안상의 문제는 클라이언트 측에서 키체인으로 토큰을 한 번 더 암호화해서 보내는 방식을 사용하면 된다.
- 앱 특성상 혼자 사용하는 앱이므로 30일 미접속 시 로그아웃 기능은 필요성이 크지 않다.
나는 그냥 내가 구현해 둔 코드를 와장창 부숴야 된다는 생각에 그냥 이대로 유지되면 좋을 것 같다고 생각하고 있었다...
결국 [30일 미접속 시 로그아웃]을 기획하신 PM님께 여쭤보게 됐다. 이후 PM님이 바로 모든 상황을 파악하시고 객관적으로 각 파트의 입장을 정리한 뒤, 프론트엔드 측의 로직 추가와 백엔드 측의 로직 리팩터링에 걸리는 시간에 대해 질문해 주셨다. 두 파트의 의견을 빠르게 취합하고 난 뒤 결론은... 필수 기능 구현을 우선하기 위해 백엔드 측에서 로직을 리팩터링 하게 됐고, 주말 사이에 열심히 고쳐서 PR을 올렸다. 아마 런칭 대신 핵심 기능 구현을 목표로 둬서 이런 결과가 나온 것 같다. 런칭까지 진행되지 못한 게 정말 아쉬웠다.
b. DTO 반환하기
첫 해커톤부터 느낀 거지만 Controller에서 API 호출 값을 반환할 때, 요청 DTO와 응답 DTO를 적절하게 만들어 두는 게 중요하다.
{isSuccess, code, message, result}로 응답을 통일해 두고, result에 DTO를 반환하는 구조다. 이때, 아래 코드처럼 DTO를 사용하지 않고 반환 값을 그대로 result에 넣어 반환하면 안 된다.
- 사실 반환할 값이 하나밖에 없어서 값만 전달해도 될 줄 알고 구현한 건데, 이렇게 구현하면 값을 뽑아낼 수가 없다고 한다.
@Slf4j
@RestController
@RequiredArgsConstructor
@RequestMapping("/member")
@Tag(name = "member", description = "회원 관련 API")
public class MemberApiController {
...
@GetMapping("/safebox")
@Operation(summary = "회원 세이프박스 조회 API", description = "헤더에 있는 토큰으로 회원을 식별하고, 회원의 세이프박스 금액 조회하는 API")
public ApiResponse<Long> getMemberSafeBox(@AuthenticationPrincipal MemberDetail memberDetail) {
return ApiResponse.onSuccess(memberDetail.getMember().getSafeBox());
}
}
프론트엔드 측에서 result 자체를 반환 값으로 들고 가는 줄 알았는데, 아무래도 응답이 JSON으로 전달되기 때문에 result 안에서도 key:value 형태로 값이 전달돼야 key 형태로 value를 뽑아낼 수 있나 보다.
- 그래서 DTO와 Converter를 만들고, Controller도 아래처럼 DTO를 반환하도록 수정했다.
@Slf4j
@RestController
@RequiredArgsConstructor
@RequestMapping("/member")
@Tag(name = "member", description = "회원 관련 API")
public class MemberApiController {
...
@GetMapping("/safebox")
@Operation(summary = "회원 세이프박스 조회 API", description = "헤더에 있는 토큰으로 회원을 식별하고, 회원의 세이프박스 금액 조회하는 API")
public ApiResponse<MemberResponseDTO.MemberSafeBoxResultDTO> getMemberSafeBox(@AuthenticationPrincipal MemberDetail memberDetail) {
Long safeBox = memberDetail.getMember().getSafeBox();
return ApiResponse.onSuccess(MemberConverter.toSafeBoxResultDTO(safeBox));
}
}
물론 리스트 그대로 반환하는 것도 안 된다. 무조건 DTO에 넣어서 반환하자.
c. Swagger
API마다 어떤 에러 코드가 발생할 수 있는지 프론트엔드 측에 알려줘야 했다.
- Controller에 Swagger 관련 애노테이션을 사용하면, 코드로도 API마다 어떤 에러 코드가 발생할 수 있는지 나타낼 수 있어서 이 방식을 쓰기로 했다. 예시는 아래와 같다.
- @ApiResponses에 @io.swagger.v3.oas.annotations.responses.ApiResponse 애노테이션을 나열하면 발생할 수 있는 에러 코드를 나타낼 수 있다.
- 토큰 관련한 에러는 전체 API에서 공통으로 발생하는 거라 노션 백엔드 공유 문서에 따로 작성해 뒀다.
@Slf4j
@RestController
@RequiredArgsConstructor
@RequestMapping("/member")
@Tag(name = "member", description = "회원 관련 API")
public class MemberApiController {
private final MemberService memberService;
@PostMapping("/signup")
@Operation(summary = "회원가입 API", description = "이메일 인증으로 회원가입을 진행하는 API (액세스 토큰 필요 없음)")
@ApiResponses({
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "COMMON200",description = "OK, 성공"),
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "MEMBER4005", description = "존재하지 않는 회원입니다.",content = @Content(schema = @Schema(implementation = ApiResponse.class))),
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "MONTHBUDGET4001", description = "한 달 예산이 존재하지 않습니다.",content = @Content(schema = @Schema(implementation = ApiResponse.class))),
})
public ApiResponse<MemberResponseDTO.MemberJoinResultDTO> join(@Valid @RequestBody MemberRequestDTO.MemberJoinDTO request) throws Exception {
Member member = memberService.joinMember(request);
memberService.connectToDayBudget(member.getId());
return ApiResponse.onSuccess(MemberConverter.toJoinResultDTO(member));
}
}
프론트엔드분들께 Swagger API 테스트 방법을 공유해드리지 않은 것도 좀 아쉬웠다. HY-THON 프로젝트에서 했던 것처럼 토큰 인증이 필요 없는 API 테스트부터 토큰 인증이 필요한 API 테스트까지 정리해서 노션 백엔드 공유 문서에 추가했으면 API 연동을 하실 때 덜 불편하셨을 것 같다는 생각이 들었다.
- 다음부턴 프로젝트 공유 문서도 템플릿을 만들어 놓고 프로젝트마다 사용해야겠다.
디자이너님과는 딱히 요청드릴 것도 없고 받은 것도 없었다.
UMC 6th 데모데이: 8.22(목)
행사 진행 및 마무리
8월 22일부터 8월 24일까지 선릉 디캠프에서 UMC 6th 데모데이가 진행됐다. 우리 팀은 첫날에 참여해서 부스를 운영했다. 하루에 10팀 정도 참여해서 다른 팀들 부스도 구경할 기회가 주어졌다. 돌아다니면서 스탬프도 찍고 앱/웹 서비스도 좀 써봤다. 가장 기억에 남는 서비스는 블로그와 코드를 합친 codiary?라는 서비스였는데, 부스 방문을 못 해서 자세히 확인하지 못해서 아쉬웠다.
UMC 부산 데모데이에도 참여할 기회가 생겼었는데 시간이 안 맞아서 아쉽게도 참여하지 않기로 했다. 아무튼 운이 좋게도 데모데이에서 상을 받게 됐다.
이번 프로젝트를 통해 정말 많은 걸 학습했고, 협업과 소통 능력도 기를 수 있었던 것 같아서 데모데이 결과와는 상관없이 정말 만족스러운 시간이 됐다고 생각한다. 앞으로 다른 팀 프로젝트에도 더 당당하게 참여할 수 있도록 다른 기술들도 공부해보려고 한다.