이미지 - 스타터스 취업 부트캠프(STARTERS) 공식 블로그

0. 마무리

서울시 중구 청계천로 24 케이스퀘어시티빌딩 웅진씽크빅 청계사옥 (이미지 - 네이버 지도)
파이널 프로젝트 발표가 진행됐던 웅진씽크빅 청계사옥 지하 2층 문봉교실 (이미지 - 유데미 스타터스 제공)

  • 드디어 길고 길었던 3개월 간의 부트캠프 일정이 모두 끝이 났다. 첫 번째 달에는 HTML, CSS, Javascript(와 리액트 아주 살짝), Java를 배웠고, 두 번째 달에는 Spring과 Git, AWS에 대해서 학습했다. (중간에 살짝 미니 프로젝트도 진행했다.) 마지막 달에는 에자일 방법론을 학습하였고 바로 파이널 프로젝트를  진행했다. (최종 테스트까지 포함해서 시험도 총 4~5번은 본 것 같다!)
  • 이렇게 나열하고 보니 정말 많은 것을 배웠고 많은 것들을 해낸 것 같아서 괜히 뿌듯하다. 결과와 상관 없이 나 자신에게 정말 고생 많았다고 말해주고 싶고, 모든 일정을 끝까지 함께 해준 동료들에게도 너무너무 고맙다고 전하고 싶다. 비록 우리의 동행은 3월 3일부로 끝이 났지만, 반드시 모두 현업에서 다시 만날 거라고 믿어 의심치 않는다!! 다들 실력 있고 열정 넘치는 개발자들이니까!!!
  • 다들 정말 정말 고생 많으셨습니다!!!!!!! 그리고 정말 재밌게 즐기다 갑니다!!!

1. 좋았던 점(Liked)

파이널 프로젝트

  • 우리 팀의 케미는 정말 완벽에 가까웠다. 초반에는 각자 이리저리 튀기도 하고 의견 차이가 쉽게 좁혀지지 않아서 서로 기분이 상하는 일도 있었다. 하지만 초반에 잠깐 이러한 과정을 겪고 나니 서로에 대한 이해도 높아지고 자연스럽게 서로가 서로를 이해하고 배려하면서 협업을 하게 되었다. (중간에 두 번 정도의 회식을 가졌는데 혹시 그 덕분이었을까...?) 그리고 이러한 팀워크를 가질 수 있었던 가장 큰 이유는 팀원 모두가 인턴에 대한 부담감과 집착을 버리고 '우리가 결국 하고 싶은 것', '우리가 최종적으로 만들어내고 싶은 것'에 대해 합의하고 그것에만 집중했기 때문이라고 생각한다. 결국 '잘 해야만 한다'는 부담감에서 벗어나 '우리가 진짜 하고 싶은 것을 한다'는 설렘과 기대감을 가지고 프로젝트에 임했던 것 같다.
  • '우리가 진짜 하고 싶은 것 / 최종적으로 만들어내고 싶은 것'에 대한 합의가 이루어지자 다른 여러 가지 부분들도 동시에 명확해졌다. 우리가 사용하고자 하는 기술 스택에 대한 제약이 사라졌으며, 여러 가지 과정과 절차에 '우리다움/ 우리생각'들이 입혀지면서 합의도 자연스럽게 이루어졌다. 항상 자기만의 언어와 방식으로 이야기하는 것을 좋아하는(잘하는) 팀장님과 도전하기를 좋아하는 팀원들의 영향력과 역할이 매우 컸다고 생각한다. 정말 즐겁고 색다른 경험이었고 많이 배울 수 있어서 너무 감사했다!
  • 뿐만 아니라 서로 하고 싶었던 부분과 잘 하는 부분이 적절히 잘 나뉘어 있었다. 나는 개인적으로 이전에 해보지 못했던 부분을 해보고 싶었는데 아니나 다를까 회원가입/로그인 부분에서 소셜 계정과 연동하는 부분을 맡게 되었다. 해당 기능에는 spring security와 oauth 2.0 기술이 사용되었는데 둘 다 처음 배워보고 적용해보는 나로서는 다소 부담이 되기도 했지만 다른 한편으로는 도전의식을 자극했다. 결과적으로 쪼금 고생을 하기는 했지만 옆에서 응원해주고 도움을 주는 팀원들 덕분에 끝내 구현을 할 수 있었고 최종 발표에서도 이에 대해 언급할 수 있어서 무척 만족스러웠다. 게다가 이러한 과정을 거치면서 개발에 대한 자신감도 정말 많이 늘은 것 같다!

우리가 제작한 서비스(Dodu)의 최종 발표 자료 일부

열정 넘치는 동료들과 강사님

  • 사실 커리큘럼 자체가 매우 좋았다고는 말하기 힘들다.왜냐면 스프링 자체가 단기간(특히 1개월은 말이 안 된다..)에 완벽하게 배워서 사용할 수 있는 프레임워크가 아니고,  요새는 개발이 전부 프론트단과 RESTful하게 통신하는 방식으로 이루어지는데 이에 대한 교육적인 고려와 구성, 지원이 부족하다고 생각했다.
  • 하지만 그럼에도 불구하고 열심히 배우고 더 많이 배우려는 사람들이 많았던 것이 정말 좋았다. 사실 나도 3개월 동안 지치고 힘들고 하기 싫은 때가 종종 있었는데 옆에서 같이 공부하는 동료들을 보고 이겨냈다. 사람들마다 위기도 있었고 각자의 우여곡절이 있었지만 결국 서로를 바라보고 서로에게 힘을 얻으며 다들 견뎌낸 것 같다. 정말 다들 존경하고 감사하고 모두가 잘 되었으면 좋겠다!!! (다들 분명 잘 될 거다... 두고 봐라!!)
  • 그리고 거의 처음이자 마지막으로 우리의 교육을 담당했던 강사님과 사적인 이야기를 해볼 수 있는 기회가 있었다. 최종 발표를 끝내고 오후에 팀별 회고를 하는 시간에 강사님께서 각 팀을 돌아다시니면서 소감을 말씀해주시고 조언도 해주셨기 때문이다.  짧은 기간 내에 정말 많은 내용을 가르치셔야 했기 때문에 내 기억 속의 강사님은 끊임없이 공부하시고 수업을 준비하시느라 바쁘신 분이셨다. 그러나 이 날만큼은 강사님께서 친히 오셔서 개개인에 대한 인상과 소감 그리고 조언 같은 것을 해주셨는데, 생각보다 학생들 개개인에 대해서 빠삭하게(?) 파악하고 계셔서 놀랐다. 직접 말씀은 하시지 않으셨지만 그만큼 학생들에 대해서 관심을 가지고 지켜보고 계셨고 속으로 많이 응원도 하고 계셨던 것 같다. 좋은 강사님 덕분에 정말 수많은 개념들이 머리속에 제대로 정리되어 저장될 수 있었고, 그것들을 활용하여 프로젝트를 무사히 마무리 할 수 있었다. 정말 감사했습니다!

 

2. 새롭게 배운점(Learned)

애자일 방법론

  • 스프링에 대해 나름(?) 깊게 배울 수 있었던 것 만큼이나 이번 교육과정에서 만족스러웠던 것은 '애자일 방법론'에 대한 학습과 실제로 프로젝트에서 적용해본 경험이었다. 이전에 어디서 주워들은 것은 많아서 '애자일이 좋다', '좋은 기업들은 다 애자일한다'는 말에 무작정 애자일에 대해 찾아보고 학습해본 적이 있었다. 하지만 그렇게 배운 지식과 정보들은 너무 뜬구름 잡는 이야기들이었다. 결국 당시에 애자일은 포기하고 개발 공부나 열심히 하자며 씁쓸하게 돌아섰던 기억이 있다.
  • 하지만 이번에 초빙된 강사님은 정말 디자인 씽킹과 애자일 방법론에 도가 트신 분 같았다. 본인은 스스로를 '퍼실리테이터'라고 칭하셨던 것 같은데 강의 방식이나 내용이 매우 신선했고, 주입식 교육에 찌들어 있던 나에게는 다소 충격적이기까지 했다. 온갖 활동과 수많은 회의를 통해 우리는 디자인 씽킹과 애자일 방법론에 대해 배우고 실제로 경험해볼 수 있었다. 애자일이란게 단기간에 익히고 쉽게 적용할 수 있는 것은 절대 아닐 것 같다는 생각이 들었다. 이러한 방법론을 적용하고 도입하기 위해서 여러 회사들과 그 구성원들은 얼마나 노력했을지,,, 
  • 우리도 결국 프로젝트 초반에 애자일하게 가기 위해서 얼마나 많은 노력을 했으며, 얼마나 많은 회의를 가졌는지 모른다. 가끔은 애자일에 너무 목을 메는게 아닌가 싶을 정도로 개발을 미뤄두면서까지 회의를 진행했다. 사실 이때가 전체 프로젝트 기간을 통틀어서 가장 힘들었다. 팀원들의 불만도 하나 둘씩 등장하기 시작했다.  하지만 (다소 길 때도 있었지만) 반복적인 회의를 거치면서 생각보다 우리가 해야할 부분이 명확해졌고, 결과적으로 이는 프로젝트가 진행되는데 굉장히 긍정적인 영향을 미쳤다. 여러 의미에서 팀장님도 고생했고, 스크럼 마스터님도 고생했고, 열심히 따라온 팀원들도 모두 고생했다.!!

스프링 부트

  • 스프링과 스프링 부트에 대해서는 이전 학습일지에서 많이 다루었으므로 자세한 내용은 생략하겠다.
  • 하지만 개인적으로 미니 프로젝트와 파이널 프로젝트에서 서로 다른 스프링 부트 버전과 기술 스택을 사용해볼 수 있어서 무척 좋았다. 미니 프로젝트에서는 Spring Boot 2와 Mybatis, JSP를 주로 사용했고 파이널 프로젝트에서는 spring boot 3과 JPA(hibernate), Thymeleaf를 주로 사용했다. 두 프로젝트에서 서로 기술 스택이 다르다보니 개인적으로 학습하는 방법과 프로젝트에 적용하는 방법에서 차이가 있었다. 미니 프로젝트 때에는 기존에 수업시간에 한 번 깊이 학습했던 내용을 복습하면서 프로젝트에 적용하는 방식이라 조금 더 복잡하고 난이도있는 기능 구현에 도전해볼 수 있었다. 하지만 파이널 프로젝트 때에는 완전히 새로운 기술을 바로바로 배우면서 프로젝트에 적용해야 했다. 그렇기 때문에 한번에 복잡한 기능을 구현하려고 하기보다는 간단한 기능들(혹은 이미 구현된 기능들)을 조금씩 차근차근히 적용해보는 방식으로 접근했다. 

3. 부족했던 부분(Lacked)

커뮤니케이션

  • 사실 해당 과정에 들어오기 전에 개인적으로 세웠던 목표가 하나 있었다. 가능한 한 많은 사람들(= 될 수 있으면 모든 사람들)과 개발 혹은 진로와 관련된 주제로 이야기 해보는 것이었다. 과정이 끝난 지금 이 시점에서 해당 목표를 돌이켜봤을 때, 한 70% 정도는 달성한 것 같다. 다소 내성적인 성격 때문에 많은 사람들과 깊은 이야기를 나누는 게 쉽지만은 않았다. 그래도 누군가와 접점이 생겼을 때는 최선을 다해서 이야기해보려고 노력했던 것 같다! 
  • 그리고 사실 주변 가까운 사람들에게도 더 하고 싶은 말들이 많았지만, '관점의 차이' 혹은 '표현 방식의 문제'로 꾹 참았던 적이 많았다. 이럴 때마다 내가 조금 더 적극적으로 표현할 수 있는 사람이었다면, 그리고 상대방의 기분을 상하지 않게 재치있게 말할 수 있는 사람이었다면 더 좋았을텐데....하는 아쉬움이 남았다.

기술에 대한 이해

  • 위에서 말했듯이 새로운 기술을 바로바로 학습하여 프로젝트에 적용했을 때는, 완벽히 이해하지 못했지만 일단 구현을 위해 그냥 가져다가 쓴 코드들이 상당히 많았다. 프로젝트 기간이 좀 더 길었다면 분명히 팀원들끼리 스터디도 하고 토이 프로젝트도 진행하면서 기술에 대한 이해도를 높였을 것이지만, 현실적으로 그럴만한 시간이 부족했다. 여전히 물음표로 남아있는 기술들에 대해서는 꼭, 반드시 다시 학습하고 정리할 필요가 있다.

4. 앞으로 할 것(Longed for)

  • 프로젝트 리팩토링⭐
  • 스프링 공부 (+ CS 공부) 
  • 동료들과 꾸준히 연락하기
  • 알고리즘 다시 시작
Don't beat yourself up
Don't need to run so fast
Sometimes we come last
but we did our best
🐰🦊

이미지 - 스타터스 취업 부트캠프(STARTERS) 공식 블로그

1. 좋았던 점(Liked)

  • 이전에 JPA를 배워본 적도 사용해본 적도 없어서 막연한 두려움을 가지고 있었다. 하지만 스터디와 멘토링을 통해 JPA에 대해 학습하였고 실제로 프로젝트에 적용해보면서 감을 잡을 수 있었다. 초기에 엔티티를 제대로 정의하기만 하면 간단한 쿼리문을 작성하고 날리는 것은 정말 쉬웠다. 하지만 더 복잡한 쿼리문과 테이블 관계에 적용하는 것은 쉽지 않기 때문에 더 많은 공부가 필요하다.

2. 새롭게 배운점(Learned)

  • Spring Security와 OAuth2를 이용하여 소셜 로그인 기능을 구현해보았다. Spring Security와 Filter 또한 이전에 학습해본 경험이 없었기 때문에 다소 어려웠지만, 여러 서적과 블로그 글, 깃헙 소스코드를 참고하여 나름대로 이해해서 코드를 작성했다. 

3. 부족했던 부분(Lacked)

  • Spring Security 개념이 생각보다 방대해서 이해하고 프로젝트에 적용하는데 상당한 어려움을 겪었다. 사실 아직도 Spring Security와 Authentication, Authorization, Filter, Interceptor 같은 것들에 대해서 완벽히 이해를 하지 못했다.

4. 앞으로 할 것(Longed for)

  • Spring Security에 대해 천천히 학습을 한 후 다시 정리해보는 시간이 필요하다. 
  • Admin 페이지 고도화 작업에 참여할 예정이다.

이미지 - 스타터스 취업 부트캠프(STARTERS) 공식 블로그

1. 좋았던 점(Liked)

  1차 스프린트가 끝나고 2차 스프린트에 돌입했다. 1차 스프린트에는 회의가 잦아서 자꾸 정체되는 느낌이 들었는데, 2차 스프린트에 들어가면서 본격적인 개발을 시작하고 프로젝트에 속도가 붙기 시작했다. 실제로 1차 스프린트 기간의 대부분을 회의와 의사결정에 할애했는데, 그 당시에는 다른 팀보다 개발이 뒤처지지 않을까 매우 불안했다. 하지만 막상 2차 스프린트 기간에 들어가니 이전에 오랫동안 논의한 내용들을 바탕으로 개발이 생각보다 빠르게 진행되었고 눈에 보이는 결과물이 하나 둘씩 나타나기 시작했다. 물론 이전에 결정됐던 내용들을 다시 끄집어내어 다시 논의하는 경우도 간혹 있었지만 (매우 피곤하고 소모적이었지만 어쩔 수 없었다..), 팀장님의 의도대로 1차 스프린트에서 회의와 의사결정에 많은 시간을 할애했던 것이 결국 긍정적인 요인으로 작용했던 것 같다. 그렇지만 더 이상의 마라톤 회의는 사양한다. 다들 많이 지쳤다.

  그리고 지금까지 멘토님과 총 3번의 멘토링을 진행했다. 우리 조의 멘토님은 국내 유명 쇼핑몰 솔루션 업체에서 프론트엔드로 재직중이셨다. 13년이라는 어마어마한 경력을 가지고 계셔서 그런지 프론트엔드 뿐만 아니라 백엔드 관련해서도 상당히 깊이 알고 계셨다. 우리는 프로젝트 방향성 그리고 진행과정, 기술스택 등 프로젝트를 기획하고 진행하면서 갖게된 모든 궁금증들을 여쭤보았고 멘토님께서는 상세하게 답변해주셨다. 프로젝트를 진행함에 있어서 든든한 지원군이 생긴 것 같아서 매우 만족스러웠다. 뿐만 아니라 해당 교육 과정에 국한되지 않고 개발자 커리어 전체를 놓고 보았을 때에도 도움이 될만한 조언들도 많이 해주셨는데 그러한 점도 굉장히 마음에 들었다. 

2. 새롭게 배운점(Learned)

  JPA와 Thymeleaf에 대해서 필요한 지식만 빠르게 속성으로 학습하였고 그것을 프로젝트에 적용해보았다. 생각보다 간단하고 편리하게 쓸 수 있는 JPA 기능들을 복잡하게 사용하고 있었다. (이 부분은 멘토님의 조언으로 바로 잡을 수 있었다. 감사합니다 멘토님!) 실제로 JPA는 Repository나 Service 단에서 메서드를 호출하여 쿼리를 날리는 부분보다 Entity(Domain)와 Dto 단에서 테이블 정의를 제대로 하는 것이 훨씬 중요했다. 팀원 대부분이 JPA 경험이 많지 않았기 때문에, 우선 엔티티를 정의해서 테이블을 만들고 거기에 dummy data를 넣는 것을 가장 최우선순위로 잡았다. 하지만 일단 엔티티를 급하게 정의하고 테이블을 생성하느라 실제 로직에 적용했을 때 '순환참조' 문제가 발생했다. 이를 해결하느라 몇 시간의 구글링을 거치고 온갖 논의가 오갔지만, 결국 우리는 해결해냈고 정말 뿌듯했다. (해결한 것에 만족하지 않고 꼭 시간을 내어서 문제의 원인을 분석하고 해결 과정을 정리하자!)

 

3. 부족했던 부분(Lacked)

  개인적으로 며칠 동안 진행된 페어 프로그래밍에 익숙해져 있어서, 막상 혼자 개발을 진행하고 기능을 구현하려 하니 좀처럼 효율이 나지 않았다.  앞으로는 정신차리고 집중해서 내게 할당된 업무를 빠르고 신속하게 처리해야 겠다.

  그리고 우리의 목표였던 RESTful API를 완성시키기 위해서는 AJAX 혹은 Axios API, Fetch API가 필수적인 것 같았다. (Thymeleaf 하나 쓴다고 쉽게 해결될 문제가 아니었다..) 현 상황에서는 이에 대한 학습과 실습이 시급한 것 같다.

  마지막으로 지금까지 수많은 마라톤 회의를 해왔지만 정작 중요한 메서드 네이밍에 대한 논의는 하지 못했다. 그러한 상황에서 막상 개발에 돌입하니 서로 짠 코드를 정확히 이해하지 못하고 협업을 하게 되는 상황이 발생했다. 빠른 시일 내에 이 문제를 바로 잡아야 한다.

4. 앞으로 할 것(Longed for)

  1. Javascript 라이브러리에 대한 학습과 적용 필요
  2. Chat과 Login / Signup 기능에 대한 논의 및 구현

 

사진: Unsplash 의 Rod Long

When life gives you lemons, make lemonade

* 유데미 바로가기 : https://bit.ly/3V220ri

* STARTERS 취업 부트캠프 공식 블로그 보러가기 : https://blog.naver.com/udemy-wjtb

본 후기는 유데미-웅진씽크빅 취업 부트캠프 3기 백엔드 과정 학습 일지 리뷰로 작성되었습니다.

이미지 - 스타터스 취업 부트캠프(STARTERS) 공식 블로그

1. 좋았던 점(Liked)

 디자인 씽킹, 에자일 방법론, 스크럼에 대해서 3일 동안 특강을 들었다. 평소 '어떻게 하면 더 나은 방법으로 (효율적으로) 개발을 할 수 있을까?'에 관한 고민이 많이 있었고 나름대로 구글링해서 얻은 정보를 가지고 여러 시도를 해보기도 했다. 예를 들어 지난 프로젝트에서 일정과 목표를 조금 더 효율적으로 설정하고 달성률을 확인하기 위해 Zenhub이라는 툴을 사용했었다.  당시 Zenhub에도 Product Backlog, Sprint Backlog와 같은 항목들이 존재했지만 에자일에 대해 제대로 이해하고 있지 못해서 이와 같은 항목들을 제대로 사용하지 못했다. 이번 주 3일이라는 짧은 기간동안 나름 에자일 프로세스를 학습하고 이해하고 직접 경험해보았으므로 파이널 프로젝트 때 최대한 따라해보면서 체화하고 내 것으로 만들어봐야 겠다.

 

 

2. 새롭게 배운점(Learned)

디자인 씽킹(Design Thinking)

: 사용자에 대한 공감과 이해를 바탕으로 문제를 정의하고, 다양한 분야의 사람들과의 협업을 통해 창의적인 해결방안을 도출해내며, 신속한 프로토타이핑과 반복적인 테스트를 통해 결과물을 만들어내는 전략이다. 대부분 '공감-문제정의-아이디어-프로토타입-검증'의 프로세스를 따른다.1)

 

에자일 방법론

: 전통적인 폭포수 방법론에서 벗어나 조금 더 유연한 프로세스로 개발하는 방법론이다. 기능 단위의 프로토타입을 기반으로 일을 하기 때문에, 프로토타입 개발이 완료될 때마다 릴리즈하고 피드백을 받고 빠르게 수정을 한다.(이러한 과정을 반복적으로 수행한다.) 에자일 방법론을 실행하는 대표적인 도구로 '스크럼'이 있는데, 스크럼은 Sprint를 기반으로 실행한다. 2)

 

스크럼

:  Sprint라는 반복적인 개발주기를 지정해서 애자일 방법을 실행한다.2)

 

프로덕트 백로그

: 사용자(=페르소나)를 정의하고 조사하여 구현해야 할 기능들을 정의한 문서다. 모든 기능들을 특정 형식에 맞춰서 줄글로 표현해야 하며, 우선순위에 따라 정렬한다. Product Owner가 작성하며 하나의 스프린트가 끝나면 해당 문서를 업데이트하여 스프린트 회의에서 제시하여야 한다.3)

스프린트 백로그

: 요구 사항(=피드백)을 태스크로 구체화한 문서이다. 원칙적으로 수정이 불가능하며 테스트 주도 개발 계획이 포함되어야 한다. 스프린트 백로그는 Sprint 회의 때 결정된다.3)  

 

3. 부족했던 부분(Lacked)

 이번 주는 프로젝트를 본격적으로 시작하는 주였기 때문에, 주제 선정과 기능 정의를 위해 며칠동안 마라톤 회의를 진행했다. 따라서 목표했던 JPA 강의를 충분히 수강하지 못했다. 토요일에도 용산 교육장에 나가서 산출물 작성에 필요한 작업과 분담한 업무를 하느라 계획했던 공부를 하지 못했다. 필요하면 잠을 좀 줄여서라도 학습에 조금 더 집중해야 겠다!

 + 그리고 틀려도 상관없으니까 내가 이해한 것을 나만의 단어, 문장으로 설명하는 습관을 기르자. 우리 팀장님은 그런 것을 굉장히 잘 하시는데...보고 배우자!

4. 앞으로 할 것(Longed for)

  • JPA 강의 완강
  • 메인 페이지 View 만들기
  • 구현 기능 역할 분담 -> 개발 시작!

 

참고

  1. 디자인 씽킹과 새로운 미래, 사람인
  2. 에자일 방법론 - 스크럼 vs 칸반 데이터팀은?, 티스토리 블로그
  3. 애자일 스크럼 정리1(스크럼 개념/백로그/번다운 차트), 티스토리 블로그

 

 


* 유데미 바로가기 : https://bit.ly/3V220ri

* STARTERS 취업 부트캠프 공식 블로그 보러가기 : https://blog.naver.com/udemy-wjtb

본 후기는 유데미-웅진씽크빅 취업 부트캠프 3기 백엔드 과정 학습 일지 리뷰로 작성되었습니다.

이미지 - 스타터스 취업 부트캠프(STARTERS) 공식 블로그

1. 좋았던 점(Liked)

  미니 프로젝트가 끝나고 파이널 프로젝트를 위한 새로운 조가 편성되었다. 이전 조원들과 미니 프로젝트가 끝난 뒤 마지막 회고 겸 리뷰 회의를 진행하자고 약속했었고, 감사하게도 다들 바빴지만 짬을 내어서 참여해주셨다. 프로젝트를 하면서 느꼈던 점, 어려웠던 점, 미처 신경쓰지 못했던 부분들을 서로 공유했고 각자 부족하다고 느꼈던 부분이 채워지는 기분이었다. 정말 안 했으면 크게 후회할 뻔 했다.

  이번주 월요일부터 수요일까지는 다른 외부 강사님이 오셔서 '클라우드와 AWS(Amazon Web Services)'에 대한 특강을 진행했다. 이전에 AWS를 사용하여 웹 어플리케이션을 배포한 경험은 있지만, 책과 구글링을 통해서 급하게 학습한 뒤에 부랴부랴 배포를 진행해서 개인적으로 아쉬움이 많이 남았다. 마음 속에 항상 '시간만 있다면 AWS에 대해서 한번 제대로 배워봐야지'라는 생각을 가지고 있었는데 운이 좋게도 (짧은 기간이었지만) 이번에  AWS 전반에 대해 배울 수 있었다.

2. 새롭게 배운점(Learned)

  미니 프로젝트 회고를 진행하면서 프로젝트에서 내가 직접 구현하지 않았던 Filter, Validation, JavaMailSender의 개념과 동작 원리에 대해 자세한 설명을 들을 수 있었다. 이러한 개념들을 조금 더 찾아보고 학습하여 다음 프로젝트 때에는 내가 직접 구현하고 적용해볼 수 있었으면 좋겠다.

  수업시간에는 클라우드와 AWS에 대해 많은 내용을 학습했다.

클라우드

막대한 양의 IT 리소스를 온디멘드(on-demand) 방식으로 인터넷을 통해 제공하고 사용한 만큼의 비용을 지불하는 방식이다. 

...


3. 부족했던 부분(Lacked)

  AWS Lambda와 Jenkins에 대해서 이전부터 궁금했는데 이번 기회에 해당 서비스들에 대해 살짝 맛을 볼 수 있었다. 하지만 Lambda는 너무 짧게 학습하고 지나쳐서 어떤 기능을 구현하기 위해 어떤 방식으로 쓰는 것이 가장 좋은 방법일지 깊이 고민해보지 못했다. 

 

4. 앞으로 할 것(Longed for)

  미니 프로젝트 결과물을 처음부터 끝까지 (구조와 절차에 대해 신경쓰고 천천히 곱씹어보면서) 혼자서 구현해볼 것이다. 현상황에서 다른 무엇을 추가로 학습하는 것보다 많은 도움이 될 것 같다.

  그리고 아무래도 새로운 팀에서는 JPA 또는 Thymeleaf와 같은 새로운 기술 스택을 도입할 것으로 보이는데, 새롭게 배워야 하고 새롭게 적용해야 하는 것이라고 너무 부담감이나 거부감을 가질 필요는 없을 것 같다. 모르면 모르는대로 최선을 다해서 구현을 하면 되는 것이고, 추가적인 도움이 필요하면 같이 학습하는 동료들에게 양해를 구하고 물어보면 된다. (그러라고 동료가 있는 것이니까...!) 너무 자기 자신을 과신해서 무리하면 안 되지만, 그렇다고 지금처럼 너무 소극적으로 참여할 필요도 없는 것 같다!

 

 


* 유데미 바로가기 : https://bit.ly/3V220ri

* STARTERS 취업 부트캠프 공식 블로그 보러가기 : https://blog.naver.com/udemy-wjtb

본 후기는 유데미-웅진씽크빅 취업 부트캠프 3기 백엔드 과정 학습 일지 리뷰로 작성되었습니다.

이미지 - 스타터스 취업 부트캠프(STARTERS) 공식 블로그

1. 좋았던 점(Liked)

   이번주에는 미니 프로젝트를 진행했다. 본격적인 파이널 프로젝트에 들어가기 앞서 이제까지 배운 내용들을 적용해볼 수 있는 좋은 기회였다. 3일 동안 진행되는 간단한 미니 프로젝트였기 때문에 조는 가까운 자리에 앉은 사람들끼리 편성되었다. 운이 좋게도 리더십있는 팀장님과 열정적이고 적극적인 팀원들로 조가 짜여서 매우 수월하고 즐겁게 프로젝트를 진행할 수 있었다. 현실적으로 어렵겠지만.. 파이널 프로젝트도 이분들과 함께 하게 된다면 정말 즐겁게 할 수 있을 것 같다. 우리가 정한 프로젝트 주제는 ''간단한 개발자 질의응답 커뮤니티'였으며 서비스명은 '배.용.남(배워서 용기있게 남 주자)'이었다. 

배용남 메인화면
배용남 프로젝트 구조

  수업시간에 배운 내용과 예제들을 활용하여 우리 서비스에 맞게 DTO, DAO, Controller, Service, Filter 등을 구성하고 코드를 작성하여 서로 연결시켰다. DAO, DTO, Controller, Service는 테이블(User, Board, Comment) 별로 분리하였고 View단에서는 JSP와 JSTL, Bootstrap 라이브러리를 사용하여 화면을 만들었다. 프로젝트 규모가 크지 않고 기능들이 그렇게 많지 않았기 때문에, 화면별로 각자 역할을 분담했다. 

  혼자서 프로젝트를 세팅하고 구조를 설계하고 코드를 작성했다면, (나도 처음이기 때문에) 잘못 설계하고 잘못 작성하여 그것을 바로잡느라 상당한 시간이 걸렸을 것이다. 하지만 팀원들과 서로 의견을 나누고 조금 더 효과적인 방법이 무엇인지 고민해보는 시간이 있었기 때문에 비교적 수월하게 설계하고 코드를 작성할 수 있었다. 

2. 새롭게 배운점(Learned)

  실제로 Controller 로직을 구현하고 URL을 매핑하여 API를 만들다보니 한 가지 드는 의문이 있었다. GET으로 어떠한 URL을 호출할 때 특정 인자값을 전달할 경우, Query Parameter(board/detail?seq=23)로 줄 수도 있고 Path Variable(board/detail/23)로 줄 수도 있다. 수업 시간에는 강사님 코드를 따라치느라 바빠서 주의깊게 보지 못했던 부분이었는데 이번 미니 프로젝트 기회에 나름대로 다시 한 번 정리해보고 적용 방법을 익힐 수 있었다.

 

Query Parameter - Controller (@RequestParam 어노테이션 사용)

@GetMapping("/board/detail")
    public ModelAndView board(@RequestParam(value="seq", required=true) int seq, HttpSession session) {
        ModelAndView mv= new ModelAndView();
     
     	...
        
        return mv;
    }

Query Parameter - View(index.jsp)

<c:forEach items="${boardList }" var="board">
<tr onclick="location.href='board/detail?seq=${board.boardSeq}'" id="ajaxtr">

 

Path Variable - Controller (@PathVariable 어노테이션 사용)

@GetMapping("/board/updateboard/{seq}")
    public ModelAndView updateBoard(@PathVariable("seq") int seq) {
        ModelAndView mv = new ModelAndView();
        
        ...
        
        return mv;
    }

Path Variable - View(detail.jsp)

<button type="button" onclick="location.href='updateboard/${board.boardSeq}'">수정하기</button>

3. 부족했던 부분(Lacked) 

  아무리 구현해야 하는 기능이 적었다고 하지만, 현실적으로 3일을 온전히 로직 작성에 사용할 수는 없었다. 초기 개발 환경 세팅과 깃 컨벤션 설정 그리고 협업 과정에서 발생하는 여러 에러와 충돌을 해결하는데 시간을 상당히 소비했기 때문이다. 돌이켜보면 '클린 코드'와 'RESTful한 API 설계 규칙'(그런데 JSP만으로 뷰단을 구성하는데도 RESTful한 설계가 필요한가...?? 한번 알아봐야 겠다..)에 대한 고려를 많이 하지 못한 것 같다. 분명 여러 메서드 사이에 중복된 로직이 존재했고 이에 대해서 팀원들과 논의해보고 해결방법을 찾아보고 싶었지만 시간적으로 여유가 없었다. 그리고 API 설계 규칙에 대해서도 충분히 논의를 하지 못해 팀원별로 API가 약간씩 상이하게 설계되고 작성되었다는 사실을 마지막 날에야 뒤늦게 파악할 수 있었다. 

 

4. 앞으로 할 것(Longed for)

 이후에 (가능하면) 팀원분들과 프로젝트를 회고하는 시간을 가지면서, 잘했던 부분들과 부족했던 부분들에 대해 의견을 나눠보고 개선할 부분이 있으면 개선을 해보면 좋을 것 같다.

 

#. DDC`23 개발자 컨퍼런스 요약

  1. 이번주 토요일(23.01.28)에 삼성역 코엑스에서 열렸던 멋쟁이사자처럼 주관 개발자 컨퍼런스다. (약 800명 참석)
  2. '성장'과 '조직 문화' 등과 관련하여 현업 개발자들(무신사, 뱅크샐러드, 강남언니, AWS, 위니브, 마인딩, 토스)의  가치관과 경험을 들을 수 있는 기회였다.
  3. 개인적으로 발표 내용은 뱅크샐러드 개발자 분과 위니브 대표님이 가장 좋았다.
  4. 솔직히 발표 세션보다 마지막의 패널 토크 세션이 훨씬 재밌고 유익했다.(딱딱하지 않은 분위기에 개발자 분들의 속마음과 경험, 조언등이 더 잘 공유되었던 것 같다)
  5. (발표 내용과는 별개로) 강남언니 개발자 분이 발표를 가장 자연스럽고 프로페셔널하게 잘 하셨다.(진짜 부럽다...)
  6. AWS는 혹시나 했는데 역시나....였다. 기대한 내가 바보
  7. 원래 안 갈까 고민했는데, 안 갔으면 정말 후회할 뻔 했다.
  8. 이런 기회들이 또 있으면 주변 사람들과 적극적으로 공유해서 더 많은 사람들과 함께 가고 싶다.
  9. 사실 이날의 메인 일정은 '고수가 잔뜩 올라간 멕시칸 타코 먹기'였다. ("비야게레로")
  10. 타코맛은 말할 것도 없고, 같이 간 일행분들이 만족해하셔서 너무 행복했다. 

* 유데미 바로가기 : https://bit.ly/3V220ri

* STARTERS 취업 부트캠프 공식 블로그 보러가기 : https://blog.naver.com/udemy-wjtb

본 후기는 유데미-웅진씽크빅 취업 부트캠프 3기 백엔드 과정 학습 일지 리뷰로 작성되었습니다.

이미지 - 스타터스 취업 부트캠프(STARTERS) 공식 블로그

9주차 학습 내용을 정리합니다.
이번주는 Mybatis 프레임워크, Multipart, AJAX 등을 학습하였습니다.
Mybatis

  Mybatis는 자바 영속성 프레임워크(Java Persistence Framework)로 프로그램 소스코드에서 SQL 문장을 분리하여 별도의 XML 파일로 저장하고 이들을 서로 연결시켜주는 방식으로 작동한다. 여기서 영속성이란 프로그램이 종료되더라도 사라지지 않는 데이터의 특성을 의미하며, 영속성 프레임워크는 '데이터의 저장, 조회, 변경, 삭제를 다루는 클래스 및 설정 파일들의 집합'을 의미한다. Mybatis를 사용하기 위해 필요한 파일은 두 가지로 ①DB 설정 XML 파일과 ② SQL-Mapping XML 파일이다.

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-config.dtd">
 <configuration>
 
	<typeAliases>
		<typeAlias type="mybatis.MemberDTO" alias="memberdto" />
	</typeAliases>

	 <environments default="mydb">
	 	<!-- datasource설정부분=tomcat제공datatsource(미리 일정 개수의 connection만들어두고 공유)
	 	mybatis datasource 설정  -->
	 	<environment id="mydb">
	 		<transactionManager type="JDBC" />
	 		<dataSource type="POOLED">
	 			<property name="driver" value="org.mariadb.jdbc.Driver"/>
	 			<property name="url" value="jdbc:mariadb://localhost:3306/memberdb"/>
	 			<property name="username" value="jdbc"/>
	 			<property name="password" value="jdbc"/>
	 		</dataSource>
	 	</environment>
	 </environments>
	 
	 <mappers>
	 	<!-- sql맵핑설정파일.xml -->
	 	<mapper resource="mybatis/sql-mapping.xml" />
	 </mappers>
	 
 </configuration>

'DB 설정 XML 파일(=mybatis-config.xml)'은 크게 Type Alias 설정부분과 DataSource 설정 부분, sql-mapping 설정 파일 연결 부분으로 구성되어 있다.

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

 <mapper namespace="member"> 
 <!-- member 테이블 컬럼명 === MemberDTO클래스 변수명 저장 -->
 <select id="memberlist" resultType="memberdto">
 	select * from member
 </select>
 
 <select id="membercount" resultType="int">
  select count(*) from member
 </select>

<select id="onemember" resultType="memberdto" parameterType="String">
	select * from member where id=#{a}
</select>

<select id="paginglist" resultType="memberdto" parameterType="int[]" >
select * from member order by indate desc limit 
<!-- 가져온 파라미터로 sql문을 만드는 것!(동적SQL생성) : num, num -->
<foreach collection="array" item="num" separator=",">
#{num}
</foreach>

</select>

<insert id="insertmember" parameterType="memberdto" >
	insert into member values(#{id}, #{pw}, #{name}, #{phone}, #{email}, #{address}, now())
</insert>

</mapper>

'SQL-Mapping XML 파일(=sql-mapping.xml)'에는 SQL 쿼리문이 포함되어야 하고 태그 속성으로 resultType과 parameterType을 지정해주어야 한다.

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import org.apache.ibatis.session.SqlSession;

public class MemberDAO {

	SqlSession session; // main으로부터 session값 받아와야함!
	
	// session setter 메서드 (외부로부터 객체 전달받기 위한 메서드)
	public void setSession(SqlSession session) {
		this.session = session;
	}

	public List<MemberDTO> memberlist() {
		return session.selectList("member.memberlist");
	}
	
	// 가급적이면 mapping.xml 파일의 이름과 동일하게 설정!
	public int membercount() {	
		return session.selectOne("member.membercount");
	}
	
	public MemberDTO onemember(String id) {
		return session.selectOne("member.onemember", id);
	}
	
	public List<MemberDTO> paginglist(int[] limit) {
		return session.selectList("paginglist", limit);
	}
	
	public int insertmember(MemberDTO dto) {
		return session.insert("insertmember", dto);
	}
    
  }

위와 같이 두 가지 필수 설정 파일들을 마련하면, DAO에서는 SqlSession에서 제공하는 CRUD 메서드와 XML태그의 id 값을 사용하여 SQL문을 매핑시킬 수 있다.

 

Multipart

 일반적으로 클라이언트에서 form 태그를 사용하여 서버로 요청을 보낼 때, 웹 브라우저는 자동으로 HTTP Request Header에 Content-Type: application/x-www-form-urlencoded를 지정하여 전송한다. 이 경우, POST 방식으로 요청을 보내더라도 폼에 입력한 내용들이 '&' 기호로 연결되어 HTTP Request Body에 담겨서 전송된다. 즉 입력된 데이터의 타입이 문자든, 숫자이든 간에 하나의 String 값으로 만들어져서 서버로 넘어가는 것이다. 그러면 서버는 Header의 Content-Type에 지정된 타입에 따라 Body에 들어있는 데이터를 해석하고 처리하게 된다.

  하지만 이러한 방식에서는 Content-Type을 한 가지 종류만 명시할 수 있고, 결국 HTTP Request Body에는 한 종류의 데이터만 담을 수 있다. 하지만 이미지나 파일 업로드의 경우, (1)파일 자체는 바이너리 데이터 형태로 전송되어야 하며 (2) 파일명, 작성자, 설명 등 추가 정보가 함께 전송되어야 한다. 이렇게 하나의  HTTP Request Body에 두 가지 종류의 데이터가 담겨야 하는 상황을 위해서 Content-Type:Multipart/form-data가 등장한 것이다.

  이를 위해서 클라이언트 측에서는 HTML를 작성할 때,

  • form태그의 enctype 속성 값으로 "multipart/form-data"를 설정하고
  • form 태그의 method 속성 값으로 "post"방식을 지정해야 하며
  • input 태그의 type 속성 값으로 "file"을 선택해야 한다.

한편 서버 측 Java Spring에는 Multipart라는 타입이 존재하지 않으므로 

  • Apache Commons IO 라이브러리 설치와
  • Apache Commons FileUpload 라이브러리 설치가 필요하다.

 

참고


* 유데미 바로가기 : https://bit.ly/3V220ri

* STARTERS 취업 부트캠프 공식 블로그 보러가기 : https://blog.naver.com/udemy-wjtb

본 후기는 유데미-웅진씽크빅 취업 부트캠프 3기 백엔드 과정 학습 일지 리뷰로 작성되었습니다.

이미지 - 스타터스 취업 부트캠프(STARTERS) 공식 블로그

8주차 학습 내용을 정리합니다.
이번주는 JSP 액션태그와 el태그, JSTL 라이브러리, Spring 기초에 대해서 학습했습니다.
JSP 액션태그

  JSP(Java Server Page)는 HTML 표준을 따르며 자바 코드를 작성할 수 있는 서버사이드 스크립트이다. 일반적인 JSP 태그를 사용하면 HTML 내에서도 자바 코드를 사용할 수 있지만, 처리해야 할 로직이 많아지고 구현이 복잡해질수록 HTML 내에 자바 코드가 많아져 가독성이 떨어진다. 웹 디자이너와 협업이 필요한 경우, 이는 큰 장애물이 될 수 있다. 그래서 등장한 태그가 바로 '액션 태그'다. JSP 내에서 자주 사용되는 자바 로직을 미리 JSP 태그로 구현하여 최대한 자바 로직 없이 JSP를 작성할 수 있도록 한 것이다.

태그 설명
<jsp:include page="x.jsp"/> 다른 JSP 파일을 현재 JSP 파일에 포함하는 태그 (동일 서버, 동일 컨텍스트)
<jsp:forward page="x.jsp/"> 다른 파일로 이동하는 태그(동일 서버, 동일 컨텍스트), 서블릿의 RequestDispatcher 클래스의 포워딩 기능을 대신하는 태그
<jsp:useBean ... /> 자바 객체 생성 태그
<jsp:setProperty /> 자바 객체 setter 메서드 호출 태그
<jsp:getProperty /> 자바 객체 getter 메서드 호출 태그
<jsp:param name="xxx" value="xxx"(문자열/정수/실수)/> include 또는 forward 시에 parameter로 값을 전달하는 태그 (1개씩만 전달 가능)
  • 자주하는 실수 :  JSP 액션 태그 안에서 HTML 주석(<!-- -->)을 사용하면 에러가 발생한다. (에러 찾기도 쉽지가 않다..) 그러므로 반드시 JSP 안에서는 가급적이면 JSP 주석(<%-- --%>)을 사용하자!

 

JSP el 태그

  Expression Language라고 하며 '표현 언어'라고도 한다. el 태그는 다음과 같은 특징을 지닌다.

  • 기존 표현식보다 편리하게 값을 출력할 수 있다
  • 변수와 여러 가지 연산자를 함께 사용할 수 있다
  • JSP 내장 객체에 저장된 속성과 자바 빈 속성을 출력할 수 있다
  • 표현 언어 자체 내장 객체도 존재한다
  • 단, JSP 스크립트 태그 안에서 선언된 변수는 곧바로 el 태그에서 가져다 사용할 수 없음!
      'JSP 내장 객체'와 'el 내장 객체'는 서로 다른 것이기 때문(=호환 불가)
    → setAttrbute()메서드 사용

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>

<h1> scopetest2.jsp </h1>

jsp a 출력(pageContext) = <%=pageContext.getAttribute("a") %><br>
jsp a 출력(request) = <%=request.getAttribute("a") %><br>
jsp a 출력(session)= <%=session.getAttribute("a") %><br>
jsp a 출력(application) = <%=application.getAttribute("a") %><br>

el a 출력(현재 페이지의 jsp태그 전달값) = ${pageScope.a }<br> <%-- jsp에서 값을 정의하고 el에서 값을 출력할 때 사용하는 것 : setAttribute / getAttribute --%>
el a 출력(이동 전의 jsp태그 전달값) = ${requestScope.a }<br>
el a 출력(세션 전달값) = ${sessionScope.a }<br>
el a 출력(어플리케이션 전달값) = ${applicationScope.a }<br>

<!-- 
el 전달 변수 해석 출력
1. pageScope 찾는다
2. requestScope 찾는다
3. sessionScope 찾는다
4. applicationScope 찾는다
 -->

</body>
</html>
  • el 태그는 특별한 명시가  없으면 모두 request 객체로 가져옴
    • 우선순위
      1. pageScope (Default ?)
           → Default는 pageScope인데 왜 request를 가져오는 걸까? 
               : pageScope는 동일한 JSP page 내에서 값을 정의(setAttribute)하고 가져올 때(getAttribute) 사용하는 것
      2. requestScope
      3. sessionScope
      4. applicationScope
JSP JSTL 라이브러리

   JSTL(JSP Standard Tag Library)는 JSP 사양을 확장한 라이브러리로, 기존 JSP 태그(=스크립틀릿)와 el 태그보다 간단하게 '조건문'과 '반복문'을 처리할 수 있다. JSTL을 사용하여 반복문과 조건문을 하나의 태그로 처리하면 JSP 페이지 내에서의 자바 코드와 복잡한 스크립틀릿이 줄어들어 가독성이 높아지고, 그 결과 뷰와 비즈니스 로직이 분리된다는 장점이 있다. 

  • 사용방법 : maven repository(링크)에서 jar 파일 다운로드 - src/main/webapp/WEB-INF/lib/ 폴더로 복사 - JSP 파일 상단에 taglib로 core 추가(<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>)
태그 설명
<c:set>  변수에 값을 할당
<c:remove> 변수 삭제
<c:out> 변수 값을 화면에 출력
<c:if test=""> 조건식 사용 → else문 없음! 다중 조건인 경우 <c:choose> 사용
<c:choose>
<c:when>
<c:otherwise>
<c:choose> : switch-case 문 처럼 사용
<c:when test="조건식1"> : 조건식 사용
<c:when test="조건식2"> : 조건식 사용
<c:otherwise> : 모든 조건문 만족하지 않은 경우 처리 (=else문)
<c:forEach> 반복문 사용
<c:forEach items="${배열명}" var="반복문내부변수명" varStatus="상태변수명" >

 


참고


* 유데미 바로가기 : https://bit.ly/3V220ri

* STARTERS 취업 부트캠프 공식 블로그 보러가기 : https://blog.naver.com/udemy-wjtb

본 후기는 유데미-웅진씽크빅 취업 부트캠프 3기 백엔드 과정 학습 일지 리뷰로 작성되었습니다.

이미지 - 스타터스 취업 부트캠프(STARTERS) 공식 블로그

7주차 학습 내용을 정리합니다.
이번주는 Servlet과 JSP에 대해서 학습하였습니다.
CGI(Common Gateway Interface)

  CGI는 웹 서버(Apache, NginX)와 사용자가 작성한 프로그램 사이의 규약, 인터페이스이다. 초기 웹 서버는 모든 사용자에게 동일한 정적인 데이터(HTML, XML, 이미지 등)만을 제공할 수 있는 정적인 웹서버였다. 하지만 웹이 발전하고 사용자들의 요구가 점차 복잡해지면서, 사용자별로 데이터를 입력받고 처리할 수 있는 동적인 웹 서버가 필요하게 되었다. 사용자로부터 입력받은 데이터를 저장하고 처리하기 위해서는 웹 서버가 외부 프로그램과 통신할 수 있어야 하는데, 이러한 연계법을 정해놓은 것이 CGI이다. 

    CGI는 크게 두가지 문제점을 가지고 있었는데,

  • 요청이 들어올 때마다 '프로세스'를 생성하여 처리하므로 서버에 부하가 쉽게 걸린다.
  • 동일한 CGI 구현체(프로그램)를 사용하더라도 요청(request)이 다르다면 여러 개의 CGI 구현체가 각각 생성된다. 

이러한 CGI의 문제점을 보완하고 성능을 개선하여 Java Servlet이 등장하게 되었다.

 

Servlet

 Servlet은 웹 어플리케이션을 만들 때 사용되는 자바 인터페이스이다. Servlet은 동적 웹 어플리케이션 컴포넌트로, Apache Tomcat이라는 웹 컨테이너 위에서 동작한다. Servlet을 통해 자바 언어로 CGI 프로그래밍을 할 수 있으며, Thread Pool 객체를 이용해 요청에 대해 제한된 개수만큼의 쓰레드를 생성하여 서버의 부하를 줄인다. 아래 그림처럼 Servlet으로 생성한 자바 클래스는 기본적으로 HttpServlet 클래스를 확장한다. 

IDE에서 Servlet을 생성하면 자동으로 제공되는 초기 템플릿
HttpServlet클래스 상속 계층도

  GenericServlet은 추상클래스로 Servlet 인터페이스와 ServletConfig 인터페이스를 구현한다. Servlet 인터페이스 내부에는 init(), destroy()와 같은 추상메서드들이 선언되어 있고, ServletConfig 인터페이스에는 파라미터와 컨텍스트와 관련된 추상 메서드들이 선언되어 있다. HttpServlet은 이러한 GenericServlet 추상클래스를 상속받아서 확장한 클래스이기 때문에 GenericServlet에서 정의된 메서드들을 오버라이딩하여 사용해야 한다.

   

Servlet Lifecycle

  클라이언트에서 요청이 들어오면, 서버에서는 Servlet 컨테이너 안에 쓰레드와 Servlet 객체를 생성한다. 최초 요청 시에만 Servlet 객체를 새로 생성(= Heap 메모리 위에 load)하고 init() 메서드를 호출한다. 이후 요청에 대해서는 이미 생성된 Servlet 객체를 재활용하기 때문에 init() 메서드를 호출하지 않는다. 이후 service() 메서드가 호출되어 doGet() 또는 doPost() 메서드가 실행되고, 해당 메서드 내부에서 로직을 통해 요청을 처리한다. (이들은 요청이 들어올 때마다 매번 호출되는 메서드들이다) 그리고 마지막으로 destroy() 메서드가 호출되면서 서버가 종료된다.

 

Servlet Mapping

 Servlet을 생성할 때 반드시 필요한 과정 중 하나로 '맵핑(Mapping)'이 있다. Servlet 클래스와 클라이언트가 사용하게 될 요청 URL을 연결하는 작업이다. 직접적인 파일명과 프로젝트 구조의 노출을 방지하고, 사용자가 사용하기 쉽도록 경로를 간단하게 축약한다.

  Servlet Mapping에는 두 가지 방법이 존재한다. 첫 번째는 독립적인 web.xml 파일에 맵핑 정보를 입력하는 방법이고, 두 번째는 Servlet 파일 내부에 어노테이션(@WebServlet)으로 맵핑명을 지정하는 방법이다. 첫 번째 방법은 web.xml이라는 별도의 파일을 열고 각종 태그를 사용하여 여러 정보를 기입해야 하므로 다소 번거롭다는 단점이 있다. 하지만 독립된 파일로 관리하기 때문에 유지보수 면에서 유리하고(URL 정보가 수정된다고 하더라도 다시 컴파일하여 배포할 필요가 없음), 어노테이션 방법과 동시에 사용하면 더 높은 우선순위를 가지고 있기 때문에 web.xml에 기록된 맵핑 정보를 바탕으로 서버가 요청을 처리한다. 따라서 규모가 큰 프로젝트에서는 web.xml을 사용한 URL Mapping을 권장한다.

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" id="WebApp_ID" version="4.0">
  <display-name>Practice_Chap02_Servlet-1</display-name>
  <welcome-file-list>
    <welcome-file>index.html</welcome-file>
    <welcome-file>index.jsp</welcome-file>
    <welcome-file>index.htm</welcome-file>
    <welcome-file>default.html</welcome-file>
    <welcome-file>default.jsp</welcome-file>
    <welcome-file>default.htm</welcome-file>
  </welcome-file-list>
  
  <!-- 1.매핑해야 될 서블릿 파일을 지정함 -->
	<servlet>
		<servlet-name>HelloWorld</servlet-name> <!-- 매핑할 이름(=닉네임) -->
		<servlet-class>sec01_exam.HelloWorld</servlet-class> <!-- 파일 경로명(=패키지명.클래스네임) -->
	</servlet>
  
  <!-- 위에서 작성한 매핑될 서블릿 파일을 url-pattern 태그를 이용하여 url창에 표식이 되도록 함 -->
  <!-- servlet-name : 위에서 서블릿 네임으로 지정한 값과 동일하게 입력해줘야 함! -->
  <servlet-mapping>
  	<servlet-name>HelloWorld</servlet-name> <!-- 위에서 지정한 매핑 이름(=닉네임)과 동일해야 함!! -->
  	<url-pattern>/hw</url-pattern>
  </servlet-mapping>
  
</web-app>

  반면 두 번째 방법은 Servlet 클래스를 생성할 때 어노테이션만을 사용해서 바로 지정해줄 수 있으므로 상대적으로 간편한 방법이다. 하지만 배포 이후에 URL 수정이 필요하게 되면 소스코드를 직접 수정해야 하기 때문에 컴파일 작업과 재배포 작업이 필수적이다. 그렇기 때문에 어노테이션을 이용한 URL Mapping 방법은 상대적으로 규모가 작고, 많은 수정이 필요하지 않은 파일에 주로 사용된다.

@WebServlet("/NowTime")
public class NowTime extends HttpServlet {
	private static final long serialVersionUID = 1L;

	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		
	}

	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
	}

}

 

 

참고

  • JSP 기초에서 실무까지 완전정복, 아이티고, 유데미강의
  • 자바 웹을 다루는 기술,  길벗, 이병승
  • [10분 테코톡] 타미의 Servlet vs Spring, 유튜브
  • [JSP/Servlet] CGI 그리고 Servlet과 JSP와의 관계, 네이버 블로그
  • 공용 게이트웨이 인터페이스, 위키백과
  • [JSP Servlet] doGet / doPost / Servlet 기본 원리 / 한글 처리, 티스토리 블로그

* 유데미 바로가기 : https://bit.ly/3V220ri

* STARTERS 취업 부트캠프 공식 블로그 보러가기 : https://blog.naver.com/udemy-wjtb

본 후기는 유데미-웅진씽크빅 취업 부트캠프 3기 백엔드 과정 학습 일지 리뷰로 작성되었습니다.

https://school.programmers.co.kr/learn/courses/30/lessons/42576?language=java

 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr

접근

  이전에 Python으로 문제를 풀었을 때는 hash함수로 hash값을 생성해서 각각의 String값을 비교했다. 하지만 Python과 달리 Java에서는 hashCode() 함수로 각 String 값에 대해 해쉬값을 생성했을 때 동일한 값이 리턴되었다. (String 값이 동일하다면 "participant".hashCode()나 new String("participant").hashCode()나 동일한 정수값이 생성되는 문제가 있었다.) 

  구글링 결과 , HashMap의 key를 participant 이름(String)으로, value를 등장한 횟수(Integer)로 정의하면 쉽게 해결할 수 있었다. 이러한 HashMap을 사용하여 key가 participant 배열에서 등장할 때마다 1을 더해주고, completion 배열에서 나타날 때마다 1씩 빼주었을 때, 마지막에 value가 1인 key값을 정답으로 출력해주면 되었다.

 

코드
import java.util.*;

class Solution {
    public String solution(String[] participant, String[] completion) {
        String answer = "";
        
        HashMap<String, Integer> map = new HashMap<>();
        
        for (String p : participant) {
            if (map.containsKey(p)) {
                map.put(p, map.get(p) + 1);
            } else {
                map.put(p, 1);
            }
        }
        
        for (String c : completion) {
            if(map.containsKey(c)) {
                map.put(c, map.get(c) - 1);
            }
        }
        
        for (String key : map.keySet()) {
            int value = map.get(key);
            if (value == 1) {
                answer = key;
            }
        }

        return answer;
    }
}

 

정리
  • containsKey(Object key)  : Map에 key값이 존재하면 true 리턴, key 값이 존재하지 않으면 false 리턴.
  • keySet() : Map에 존재하는 key의 값들을 리턴. 주로 Map을 순회할 때 사용. 
참고

+ Recent posts