728x90

안녕하세요!

정보글 작성 겸 스스로 회고 겸 부캠 후기글을 작성하고

지원서 작성과 코테, 면접 까지 지원 과정에 대해서 더 상세히 기록해보고자 합니다.

이번 글도 제 개인적 후기와 느낀 점 등을 중심으로 작성하였습니다 :)

 

1. 서류 작성

서류 지원 시에 작성해야 하는 자소서는 총 2항목 이었습니다.

 

1. IT's Your life에 지원하신 동기와 과정 수료 후, 이루고 싶은 취업계획을 작성해 주십시오. (최대 500자 입력가능)

후기글에도 적었듯이

저는 이 항목에서 저에게 부족한 점을 두괄식으로 강조해서 쓰고

부캠이 나의 이 부족한 점을 어떻게 채워줄 수 있는지 어필했습니다.

 

요즘 취준생들 중에 인턴이나 현장실습 등 실무 경험 갖고 계신 분들이 정말 많은데..

저는 이 경험이 없었어서 '실무 경험 없음'이 나의 부족한 점인데 kb 부캠의 실무형 교육으로 보완하고 싶습니다.

이렇게 강조했습니당 

 

 

2. SW 관련 경험 중 어려웠던 과제와 해결 방안에 대해 작성하고, 교육 과정을 통해 향후 어떤 개발자로 성장하고 싶은지 작성해 주십시오. ※SW 관련 경험 : SW 개발, SW 프로젝트 및 경진대회 경험(참여, 수상 등), IT 관련 자격증 취득 등 (최소 1자, 최대 500자 입력가능)

저는 전공자였으므로 이 항목에서는 졸업 작품으로 했던 개인 프로젝트 내용을 적었습니다.

개인 플젝에서 기술적 트러블 슈팅 겪었던 내용을 적었습니다.

 

=> 총평

두괄식, 간결한 문장 등에 유의해서 적었고

자소서를 엄청 잘 쓰는게 중요하다기보다는 평균 정도만 적으면 서류 합격은 가능한 것 같습니당

 

 

---

 

 

 

2. 코테 (전공자 sw적성진단)

전공자 sw진단은 코딩테스트 형식입니다!

6기는 플랫폼이 프로그래머스가 아니라 마이다스로 봤구요

마이다스는 특이한게 한 문제 내에 5가지의 단계로 구성되어 있습니다.

 

예를 들어 1번문제면

1-1번 a와 b의 최소공배수를 구하시오.

1-2번 a와 b의 최소공배수와 최대공약수를 구하시오.

 

이런 식으로 앞의 내용을 유지한 채 점점 디벨롭되는 형식입니다.

 

저는 그 전부터 코테를 계속 공부해오긴 했지만 그렇게 잘하는 건 아니었구요,,ㅎ

 

총 2문제에서

1번 문제 4단계

2번 문제 3단계

이 정도로 풀었던 것 같습니다.

 

꼭 한 문제를 5단계까지 맞추지 않아도 두 문제 골고루 푸시면 합격 가능한 것 같아요.

 

 

---

 

 

 

3. 대면 면접

마지막으로 대면면접 단계입니다!

전형을 할 때는 몰랐는데 이렇게 글로 적으니 정말.. 부캠 교육 하나 듣는 것도 쉬운 일이 아니네요

 

대면 면접은 실제 교육을 듣게 될 어린이대공원 교육장에서 진행됩니다!

5층 각 강의실에서 조 별로 대기, 6층으로 올라가서 각 방에서 면접 진행하는 순서이고

 

음.. 기억나는 내용을 적어보면

1. 복장은 정장 ~ 비즈니스 캐주얼까지 다양

그냥 정장입으면 무난할 것 같아요.

2. 5층 대기실에서 앉아서 대기하다가 모든 짐을 들고 6층으로 이동, 면접장 밖에 짐을 두고 들어갑니다.

3. 면접 구조는 면접관2 : 면접자 4으로 다대다 면접입니다.

4. 답변 순서는 왼쪽부터, 오른쪽부터 번갈아가면서 지목해주셨습니다.

5. 방마다 면접 분위기가 다를 것 같긴한데 저희 방은 꽤 부드럽게 해주셨습니다.

 

6. 면접 기출 질문

자기소개

교육 커리큘럼을 통해 어떤 개발자가 되고픈지

개인질문(졸업 여부, 이전에 들은 교육, 해본 프로젝트 등)

왜 개발자가 되고싶은지, 좋아하는 언어, 왜

협업할 때 팀장, 팀원, 갈등 해결 경험

교육 후 바로 취업할 건지 다른 교육을 더 들을건지(솔직히 답해달라고 함)

마지막 하고 싶은 말

 

제가 기억나는 면접 내용은 이 정도인데

교육 들으면서 친구들과 이야기해보니 면접장 마다 분위기도 내용도 달랐던 것 같아요.

저희 조는 기술 질문은 들어오지 않았는데 다른 곳에서는 프로젝트 기술 관련해서도 물어봤다고 들었습니다.

 

저는 개인질문으로 프론트와 백 중에서 왜 백엔드 개발자로 진로를 정했는가

이 질문을 받았습니다.

 

정말 이 교육을 수강하고 싶은지를 확인하는 전체적으로 평이한 면접이니 너무 긴장하지마시고 답변하시면

좋은 결과 얻으실 수 있을 것 같습니다.

 

추가적으로 드릴 팁은

1. 케이비 아이티즈 유얼 라이프 입니다.

잇츠가 아니라 아이티즈로 발음한다는 사실

실제로 저희 면접 조에서는 자기소개 때 제 앞의 3분이 모두 잇츠라고 하셨는데

이거는 지원자 입장에서 헷갈릴 수 있는 부분이니 크게 신경쓰시지 않는 것 같아요ㅎㅎ

(같은 면접 조에 잇츠라고 하신 분도 합격하신 것 같앗어요)

 

2. 성실히 교육 이수할 의지, 교육 후 취업할 의지

이 두개만 잘 전달되어도 합격 확률이 올라갈 것 같아요.

면접의 마지막쯤에, 여자 면접관 분께서 굉장히 따스하게

"합격하고 교육을 듣게되면 6개월이라는 긴 시간동안 중간에 느슨해지거나 마음이 헤이해질 수 있는데

지금 이 면접장에서 가졌던 열정을 생각하고 힘내주면 좋겠다"

이렇게 말씀해주셨는데

 

실제로 교육을 듣다보면 중간부터 출석을 자주 빠지는 분, 수업을 하나도 안듣는 분, 프로젝트에 성실히 참여하지 않는 분 등

초기의 열정을 잃어버리는 분들도 꽤 있었습니다.

매니저님들, 강사님들께서는 이 부트캠프 과정을 최대한 백퍼센트 활용해서 누구보다 교육과 취업을 원하는 분들을 수강생으로 맞이하길 원하실테니

열정과 간절함(?), 성실성 등을 초롱초롱하게 어필한다면

좋게 봐주실 것 같아요 :)

 

화이팅!

 

 

 

 

728x90
728x90

안녕하세요.
KB IT’s YOUR LIFE 6기 교육을 수강하고 우수수료생으로 수료한 ‘토누’입니다.
 
최근 7기 모집 공고가 올라온 걸 보고,
6기 수강생이자 우수수료생으로서 지원 과정부터 교육 전반, 종합실무 프로젝트, 취업 연계까지
제가 직접 경험한 내용을 한 번 정리해두면 도움이 되겠다 싶어 이 글을 쓰게 되었습니다.
 
이미 KB 교육은 서포터즈 분들의 홍보 글이 많이 올라와 있지만,
이 글은 서포터즈가 아닌 수강생 개인이 자발적으로 남기는 솔직 후기라는 점을 먼저 말씀드리겠습니다.
 
각 항목별로 기억나는 내용을 최대한 자세히 적다 보니 글이 조금 길 수 있습니다.
7기 지원을 고민 중이신 분들께 현실적인 판단 자료가 되었으면 좋겠습니다.
 
+ 제가 주저리주저리 글을 적은 다음에 지피티로 말투 통일, 글 정리를 시키고 다시 직접 검토했습니다.
중간에 어색한 말투가 있어도 양해해주시고 추가적으로 궁금한 점은 댓글로 달아주세요!


1. 6기 지원 과정

1-1. 서류 전형

서류에서는

“왜 이 부트캠프가 지금 나에게 필요한가”
를 얼마나 솔직하게, 구체적으로 쓰느냐가 중요하다고 느꼈습니다.

저는 취업 준비 과정에서

  • 실무 경험 부족
  • 프로젝트 경험의 깊이 부족
    을 명확한 한계로 인식하고 있었고,

종합실무 프로젝트 + 현직자 멘토링을 통해
이 부분을 보완하고 싶다는 점

을 솔직하게 작성했습니다.
 
✔️ ‘합격용 문장’보다는 현재 내 상황과 필요성을 정확히 설명하는 게 더 중요해 보였습니다.




1-2. 면접 후기

  • 면접관 2 : 면접자 4 구조, 한 타임에 여러 방에서 면접 진행
  • 면접관은 멀티캠퍼스 매니저님, 교육 대표 강사님, 각 반 담당 강사님 등으로 구성
  • 복장은 대부분 정장 (일반 공채 면접과 유사한 분위기)

면접 분위기는 전반적으로
✔️ 딱딱하지도, 과하게 유머러스하지도 않은 담백하고 솔직한 분위기였습니다.
 
주요 질문은 다음과 같았습니다.

  • 왜 이 부트캠프를 지원했는지
  • 현재 취업 준비 상황
  • 기존에 들었던 교육이 있는지
  • 이미 다른 교육을 들었는데 왜 또 이 교육을 듣고 싶은지
  • 졸업 여부(졸업 전/졸업 유예/졸업 후), 공백기, 취준 경험 등

👉 “이 교육 이후 바로 취업할 의지가 있는지”를 꽤 중요하게 보신다는 인상을 받았습니다.
 
개인적으로 느낀 선호 수강생 상

  • 졸업 직후 or 졸업 유예 상태
  • 부트캠프 경험이 많지 않은 사람
  • 교육 수료 후 바로 취업을 목표로 하는 사람
    이었습니다.
면접 날
면접 후 선물

1-3. 최종 합격

저는

  • 21~24년 대학 재학
  • 25년 상반기 졸업 유예
  • 24년 하반기 취준 경험을 통해 느낀 보완점을
    → **“부트캠프에서 어떻게 채울 것인지”**를 비교적 명확하게 어필했고
  • 나이대(여자 02년생)도 부트캠프 수강 시기와 잘 맞았다고 생각합니다.

👉 결과적으로 **최종 합격(최초 합격)**했습니다.
참고로

  • 최종 합격 발표 이후
  • OT 전
  • 교육 시작 후 약 1주일 정도까지

추가 합격도 꽤 많이 도는 편이니 예비 합격이신 분들도 너무 걱정하지 않으셔도 될 것 같습니다.


 

2. 입교 과정 & 출석 관련

2-1. OT

  • 장소: 여의도 KB국민은행 신관
  • 착석은 도착 순서대로(회차별로 앉지않음)
  • KB 부트캠프 출신 현직자 발표, 교육 소개, 간단한 레크리에이션 진행
  • 눈 마사지기 기념품도 제공됨 
kb 신관

2-2. 교육 장소 & 시간

  • 장소: 멀티캠퍼스 어린이대공원역
  • 교육 시간: 09:00 ~ 18:00
    • 실제 수업은 09:10 ~ 17:50

시설은

  • 5층, 6층 사용
  • 각 층별 4개 반
  • 5층에 라운지, 얼음 정수기 있음

추가로 5층하고 6층은 시설이 조금 다릅니다!
 
5층 강의실은 pc가 주어지고
6층 강의실은 노트북이 주어져요(강의실에서만 사용 가능)
 
5층 강의실은 아래 사진처럼 2씩 자리, 강의실이 크다는 장점이 있지만
앞뒷자리 간 소통이나 플젝하기에는 조금 불편했구요.
 
6층 강의실은 책상이 중고등학교 책걸상처럼 조금 더 작고 전체적으로 교실이 작아서
조금 답답하지만 이야기하거나 플젝할 때 모이기 좋아서 
부캠 초반에 6층 회차들이 빠르게 친해졌다고 들었습니다.

5층 강의실

 

내 자리

2-3. 출석 & 휴가

  • 월 1회 휴가 제공 (월차 개념, 누적 사용 가능, 다음 달 것 미리 사용은 불가)
  • 병가, 예비군, 열차 지연 등도 모두 출석 인정
  • 취준 일정(면접/코테)도 증빙 시 출석 처리 가능

출석 관련해서는 교육 시작 이후 자세히 공지해주시며 매니저님들께 여쭤보면 모두 자세히 알려주십니다.


3. 3월 적응 기간 & 스터디

3-1. 회차별 이벤트

초반 적응을 위해

  • 쉬는 시간 / 점심 시간에
  • 미니 이벤트, 스피드 퀴즈 등
    을 회차별로 준비해 주십니다.

3-2. 3월 회식 지원

3월에는

  • 각 회차별로 친해질 수 있도록
  • 회식비 지원이 한 번 제공됩니다.

 

플젝 중 깜짝 간식 이벤트

3-3. 스터디 지원

부트캠프에서 스터디 지원금을 주십니다. 
 
교육에서 사용하는 플랫폼인 슬랙을 통해

  • CS 스터디
  • 코딩 테스트 스터디
  • 수업 복습 스터디
    등 다양한 스터디 모집 글이 계속 올라옵니다.

👉 개인적인 추천

  • 같은 반 사람들끼리만 하는 스터디보다는
  • 다른 회차(다른 반) 사람들과 하는 스터디 1개 이상 추천

이유는

  • 인맥이 훨씬 넓어짐
  • 정보 공유 폭이 커짐
  • 같은 반만 하면 생각보다 쉽게 느슨해짐

4. 교과목 수업

  • 매일 09:00 ~ 18:00 교과목 수업
  • 교과목별 시험 존재
  • 매주 금요일 코딩테스트 수업 진행

(교과목은 공고글에 나와있는 교과목을 순서대로 진행하며 구체적인 일정표 엑셀을 공유해주실 거예요
전공반 기준 오전에는 대표 강사님 수업, 오후에는 각 회차별 담당 강사님의 수업 또는 자습을 진행합니다.
교육을 듣다보면 오전부터 수업을 안듣고 자습하는 사람, 수업을 열심히 듣는 사람, 딴짓하는 사람 등등 다양합니다..
어떻게든 본인에게 가장 잘 맞는 방법으로 꾸준히 공부하시면 될거예요


5. 종합실무 프로젝트

5-1. 팀 빌딩 방식

  • 랜덤 팀 빌딩
  • 층별 모든 회차 수강생 혼합

예를 들어
5층에 11·12·13·18회차가 있다면

  • 11회차 1명
  • 12회차 1명
  • 13회차 2명
  • 18회차 2명
    → 이런 식으로 한 팀 구성

성별, 교과목 수업 성적, 성격, 전공자 비전공자 혼합 등을 고려해
강사님들이 직접 팀을 구성한다고 들었습니다.
 
이 구조 때문에 프로젝트를 진행하다보면

  • 한 다리만 건너면 다 아는 구조가 되고
  • 좋든 나쁘든 소문도 굉장히 빠르게 날 수 있습니다

5-2. 프로젝트 팀의 분위기

교육 초반 같은 회차 사람들은 ‘친구’ 느낌이라면
프로젝트 팀은  ‘동료’에 가까운 느낌입니다.
 
물론 각 팀마다

  • 정말 친해진 팀
  • 싸웠던 팀
  • 끝까지 어색한 팀
  • 딱 일만 한 팀
    → 등 케이스가 정말 다양합니다.

호칭도 팀마다 다르고 바로 형누나로 반말을 한 팀도 있습니다.
 
저희 팀은 끝까지 존대 + ~님으로 소통했지만
수료 후에도 연말에 회식할 정도로 친하게 지내고 있습니다.


5-3. 프로젝트 진행

  • 팀 인원: 보통 5~7명
  • 역할 분담:
    • FE / BE 분리
    • or 기능별 풀스택 분담 (팀마다 다름)
  • 기간: 6주 정도

5-4. 현직자 멘토링

각 반 마다 KB 현직자 멘토님, 금융권 멘토님, 빅테크?멘토님 총 3분이 들어오십니다.
 
저희 반 같은 경우에는

  • KB국민은행 현직자 멘토님
  • 우리은행 현직자 멘토님
  • LINE 현직자 멘토님

이렇게 들어오셨고
 
멘토링은 총 3회 진행되며
프로젝트 방향성 잡는 데 큰 도움이 됩니다.
 
보통 1회차는 아이디어 구상 , 기능명세서 등을 확인하고 방향성 조언을 해주시고
2회차 때는 각 팀별 고민이 있으면 들어주시고 기능 설계부터 기술적 고민까지 저희가 궁금한 내용을 모두 들어주십니다.
이때는 각 팀, 멘토님 한분 당 한시간 정도? 넉넉하게 이야기할 시간이 있어서 프로젝트 질문 뿐만 아니라 현업에 대해 궁금한 점, 멘토님들 회사에서 사용하는 방식, 취업 관련 질문 등 알려주실 수 있는 만큼 잘 알려주시기 때문에
궁금한 점, 질문 사항 등 많이 여쭤볼 수록 도움이 됩니다.


5-5. 최종 발표 & 최우수팀

  • 마지막 날 PPT 발표
    • 프로젝트 소개
    • 문서화
    • 협업 방식
    • 개발 방식
    • 트러블슈팅
    • 시연 영상 등등
  • 발표 후 심사위원 질의응답

심사위원 구성

  • 현직자 멘토 + 강사님으로 총 3인

각 반마다 4팀 중
👉 최우수팀 1팀 선정
총 10반 → 최우수팀 10팀
이 팀들은

  • 우수수료생 가점
  • 제주도 2박 3일 연수 혜택
종합실무 프로젝트 최우수상 수상

5-6. 개인적으로 생각하는 우리 팀의 수상 이유 + 최종 발표 때 들은 투표 이유

① UI & 발표 자료 완성도

  • KB 금융그룹 캐릭터와 메인 컬러를 활용한 귀여운 UI (각 팀마다 토스같이 깔끔한 UI, 귀여운 UI등 컨셉이 있는데 저희는 좀 귀여운 느낌으로 구현한 UI가 잘 전달된것 같아요)
  • 발표 자료 퀄리티가 높았음(저희 팀같은 경우에는 금손 팀원분께서 진짜 예쁘게 잘 만들어주셨습니다.. 다른 팀들은 각자 나눠서 제작 후에 합치거나 PPT 담당을 정하거나 했던 것 같은데 이렇게 저희 팀 같은 경우에는 행운으로 금손 팀원분이 계셔서 발표 자료를 맡아서 만들어주셨습니다)

② 명확한 컨셉

  • ‘자산관리 게이미피케이션 서비스’
  • 이것 하나에만 집중
  • 불필요한 기능 과감히 제거

종합실무 프로젝트 때 우리반, 다른 반을 포함해서 멘토님들께 공통적으로 들은 피드백이
불필요한 기능, 다른 팀 다 하는 기능은 없애고 핵심 기능에 집중해야한다 였습니다.
(예를 들어 로그인, 소셜 로그인, 이메일 인증 등 개발하고 나면 뿌듯하고 꼭 필요한 기능이지만
  이거를 안하는 팀은 없으니 이런 거를 우리가 구현한 기능!하기에는 애매합니다)
(커뮤니티, 캐릭터, ai 연관 등은 거의 모든 팀이 고려합니다)
 
저희도 처음에는 자산관리, 뉴스레터, 커뮤니티 등등 메인 기능만 3~4개씩 정했는데 1차 멘토링 때 멘토님들께 모두 컷..당하고
그 이후로는 게임을 통한 자산관리(1:1매칭)이라는 핵심 기능을 깊게 디벨롭해서 2차 멘토링 때 주제가 확실히 잡히고 전달력이 좋아졌다는 칭찬을 받았습니다. 
 
프로젝트를 딱 시작하면 여기저기서 엄청난 기능을 몇개씩하고 주제를 뭘한다더라 난리가 나는데
이 때 우리 팀만의 차별성있는 주제를 확실히 정해야 끝까지 완성도있는 결과물을 낼 수 있는 것 같아요.
 
③ 기술적 고민 & 멘토링 활용

  • 우리 팀만의 매칭 알고리즘 설명
  • 교과목 수업에서 배운 내용 실제 적용
  • 멘토링에서 받은 피드백 → 결과로 연결한 과정 명확히 설명
  • (저희팀은 외부 API 서버를 연동하고 목서버나 목데이터를 추가해야하나 고민하던 시점이 있었는데 이걸 두번째 멘토링 때 멘토님께 여쭤봤고 멘토님께서 토스 블로그에 나와있는 목서버를 활용하는 방법을 알려주셨습니다. 이를 실제로 활용해서 우리팀에 맞는 목서버를 구축했습니다. 이런 고민과 시행착오의 과정을 발표자료에 담아서 우리가 고민했던 내용에 멘토링 세션때의 피드백을 활용해서 어떤 결과를 냈다 이렇게 차별점을 풀어냈습니다.)

👉 심사위원이 강사님 + 멘토님이기 때문에
교육 과정과의 연결성이 중요하다고 느꼈습니다.
 

제주도 여행을 보내준다

 
 

제대로 즐기는 우리팀

6. 우수수료생

  • 출석
  • 시험 점수
  • 가점(최우수팀, 우수훈련생, 공모전 수상 등)

등을 종합해  20% 선정
✔️ 과락만 없다면
✔️ 가점 하나만 있어도
우수수료생으로 선정되는 경우가 많아 보였습니다.
혜택

  • 5년 이내 1회 KB국민은행 IT 직무 서류 전형 면제

7. 교육 이후 지원

1️⃣ 시현하다 프로필 사진 촬영

  • 이력서 / 스타트업용 프로필 사진 촬영 가능

2️⃣ 원티드 컨설턴트 취업 상담 (5회)

  • 이력서
  • 자소서
  • 면접 준비 등 자유롭게 궁금한 점 모두 질문 가능

3️⃣ 수료 후에도

  • 슬랙, 문자
    등으로 취업 연계, 멘토링 정보 안내가 계속 오고 있음 
마지막 일정, 수료식

8. 취준 병행 & 취업 결과

8-1. 교육 중 취준

  • 평일 면접/코테 → 증빙 제출 시 출석 인정
  • 횟수 제한 없음

8-2. 교육 이후 취업

이 교육을 들었다고
❌ 갑자기 합격률 폭증
❌ 무조건 대기업 취업
이런 건 아닙니다.
다만

  • 이력서에 교육 + 프로젝트 + 수상 추가 가능
  • 자소서 소재로 활용 가능한 경험 대폭 증가

👉 중소~중견 취업에는 확실히 도움이 됨
👉 대기업은 여전히 개인 역량이 핵심
 
제 개인 결과로는

  • 중소~중견: 서류 합격률 10% 이상, 면접 참석한 곳 모두 합격
  • 대기업: 이전 시즌과 비슷한 수준 유지

9. 종합 후기

종합적으로 봤을 때,
아래에 해당한다면 수강을 추천합니다.
 
1️⃣ 졸업(유예) 후 취업이 바로 되지 않은 상황
2️⃣ 금융권 IT 취업을 목표로 하는 경우
3️⃣ 공백기 관리 + 지원금 혜택이 필요한 경우
 
저는
KB 교육을 선택한 것에 대해 전혀 후회 없고, 만족도가 높았습니다.
 
코로나 학번으로 대학에서 사람을 많이 만나고 협업하는 경험이 부족했는데,
이 부트캠프를 통해

  • 꾸준히 공부하는 습관
  • 다양한 사람들과의 협업
  • 소중한 인연
    을 얻을 수 있었습니다.

그럼에도 불구하고 느낀 단점

① 다른 부트캠프도 많다

  • 싸피같은 경우는 (월 100만 원 지원)으로 지원금 혜택이 좋고
  • 전원 서류 면제권을 주는 부트캠프 등
    → 개인 상황에 따라 더 맞는 선택지가 있을 수 있음

② 강사님 ‘뽑기 운’

  • 반마다 강사님 스타일, 경력 차이가 큼
  • 나와 맞지 않으면 아쉬움이 클 수 있음
    (이 부분은 개인적으로 가장 아쉬웠습니다)

7기 지원을 고민하시는 분들께
이 글이 조금이나마 도움이 되었길 바랍니다.
 
정리 끝!

읽어주셔서 감사합니다 😊

728x90
728x90

 

1. package org.scoula.security.account.domain;

AuthVO

@Data
public class AuthVO implements GrantedAuthority {
    private String username;
    private String auth;

    @Override
    public String getAuthority() {
        return auth;
    }
}

 

  • GrantedAuthority 인터페이스 구현
    • getAuthority() 로 권한 정보 추출

 

CustomUser

@Getter @Setter
public class CustomUser extends User {
    //실질적인 사용자 데이터
    private MemberVO member;

    public CustomUser(String username, String password, Collection<? extends GrantedAuthority> authorities) {
        super(username, password, authorities);
    }

    public CustomUser(MemberVO vo) {
        super(vo.getUsername(), vo.getPassword(), vo.getAuthList());
        this.member = vo;
    }
}
  • CustomUser(MemberVO vo)
    • MemberVO 를 User로 변환

 


 

 

2.  package org.scoula.security.account.dto;

 

LoginDTO

public class LoginDTO {
    public String username;
    public String password;

    public static LoginDTO of(HttpServletRequest request) {
        ObjectMapper om = new ObjectMapper();
        try {
            return om.readValue(request.getInputStream(), LoginDTO.class);
        } catch (Exception e) {
            e.printStackTrace();
            throw new BadCredentialsException("username or password is incorrect");
        }
    }
}
  • request body에서 username, password 역직렬화(JSON 문자열 → 자바 객체로 변환하는 것)
  • username이나 password가 틀리면 여기서 Exception 발생
  • JwtUsernamePasswordAuthenticationFilter의 attemptAuthentication에서 사용

 


 

 

3.  package org.scoula.security.account.mapper;

 

UserDetailsMapper

public interface UserDetailsMapper {
    public MemberVO get(String username);
}

 

  • tbl_member 테이블과 tbl_member_auth 테이블을 Join 처리
  • get()은 CustomUserDetailsService에서 username으로 MemberVo를 조회하는데 사용
MemberVO vo = mapper.get(username);

 

 


 

 

4. package org.scoula.security.filter;

 

 

 

JwtUsernamePasswordAuthenticationFilter

public class JwtUsernamePasswordAuthenticationFilter extends UsernamePasswordAuthenticationFilter {

    public JwtUsernamePasswordAuthenticationFilter(AuthenticationManager authenticationManager, LoginSuccessHandler loginSuccessHandler, LoginFailureHandler loginFailureHandler) {
        super(authenticationManager);

        setFilterProcessesUrl("/api/auth/login");
        setAuthenticationSuccessHandler(loginSuccessHandler);
        setAuthenticationFailureHandler(loginFailureHandler);
    }

    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {

        LoginDTO login = LoginDTO.of(request);

        UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(login.getUsername(), login.getPassword());

        return getAuthenticationManager().authenticate(authenticationToken);
    }
}

 

  • 로그인 요청 경로 지정 : /api/auth/login
  • 로그인 성공 처리기, 로그인 실패 처리기 등록

 

  • attemptAuthentication() 메서드
    • 등록한 인증 url로 요청이 오면 호출
    • 요청에서 username, password 부분을 추출
    • UsernamePasswordAuthenticationToken 구성
    • AuthenticationManager에게 인증 요청
    • 인증경과 Authentication을 리턴

필터 구조도


 

 

5. package org.scoula.security.handler, service, util;

 

 


🎁 전체 코드 => 깃허브 업로드 예정

 

 

 

🎁 패키지 전체 요약

security 패키지 전체 주요 설명

 


로그인 처리 전체 흐름

 

🐻‍❄️ 프론트엔드

stores/auth.js 에서 백엔드 서버 '/api/auth/login’  경로로 POST 요청 전송
const login = async (member) => {
    const { data } = await axios.post('/api/auth/login', member);
    state.value = { ...data };
    localStorage.setItem('auth', JSON.stringify(state.value));
  };

 

 

🐻 백엔드

1️⃣ AuthenticationErrorFilter

필터 체인 초입에 위치 / 토큰이 만료되었거나 유효하지 않은 경우 예외 발생, 예외 응답 반환

 

2️⃣ JwtAuthenticationFilter

요청 헤더에 토큰이 존재하는 지 검사 / 토큰에서 username 추출

 

 

3️⃣ JwtUsernamePasswordAuthenticationFilter

약속된 login url 요청 절차 수행

  • 요청이 JwtUsernamePasswordAuthenticationFilter.attemptAuthentication()로 전달됨.
  • LoginDTO.of(request)를 통해 요청의 username, password를 꺼냄.
  • 이 정보로 UsernamePasswordAuthenticationToken을 생성하여 AuthenticationManager에게 전달 → 로그인 인증 시도.
  • 인증 성공 → LoginSuccessHandler 동작
  • 인증 실패 → LoginFailureHandler 동작
@Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
            throws AuthenticationException {

        // 요청 BODY의 JSON에서 username, password  LoginDTO
        LoginDTO login = LoginDTO.of(request);

        // 인증 토큰(UsernamePasswordAuthenticationToken) 구성
        UsernamePasswordAuthenticationToken authenticationToken =
                new UsernamePasswordAuthenticationToken(login.getUsername(), login.getPassword());

        // AuthenticationManager에게 인증 요청
        return getAuthenticationManager().authenticate(authenticationToken);
    }

 

 

4️⃣-1) LoginSuccessHandler

public class LoginSuccessHandler implements AuthenticationSuccessHandler {
    private final JwtProcessor jwtProcessor;

    private AuthResultDTO makeAuthResult(CustomUser user) {
        String username = user.getUsername();
        // 토큰 생성
        String token = jwtProcessor.generateToken(username);
        // 토큰 + 사용자 기본 정보 (사용자명, ...)를 묶어서 AuthResultDTO 구성
        return new AuthResultDTO(token, UserInfoDTO.of(user.getMember()));
    }

    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
                                        Authentication authentication) throws IOException, ServletException {
        // 인증 결과 Principal
        CustomUser user = (CustomUser) authentication.getPrincipal();

        // 인증 성공 결과를 JSON으로 직접 응답
        AuthResultDTO result = makeAuthResult(user);
        JsonResponse.send(response, result);
    }

}

 

public static <T> void send(HttpServletResponse response, T result) throws IOException {
        ObjectMapper om = new ObjectMapper();
        response.setContentType("application/json;charset=UTF-8");
        Writer out = response.getWriter();
        out.write(om.writeValueAsString(result));
        out.flush();
    }

 

 

4️⃣-2) LoginFailureHandler

@Override
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
                                        AuthenticationException exception) throws IOException, ServletException {

        JsonResponse.sendError(response, HttpStatus.UNAUTHORIZED, "사용자 ID 또는 비밀번호가 일치하지 않습니다.");
    }

 

public static void sendError(HttpServletResponse response, HttpStatus status, String message) throws IOException {
        response.setStatus(status.value());
        response.setContentType("application/json;charset=UTF-8");
        Writer out = response.getWriter();
        out.write(message);
        out.flush();
    }

 

728x90
728x90

Spring Security

 

[등장 배경]

1. 보안 요구 증가

    웹 애플리케이션이 점점 복잡해지고 민감한 정보를 다루게 되면서, 인증 인가에 대한 처리 필요

 

2. 보안을 직접 구현하기에는 에러 발생 가능성 증가 & 유지보수 어려움 등 문제 다수

 

3. Spring에 통합되어 동작하면서도 보안 기능을 일관되게 제공할 수 있는 프레임워크 필요


  • 필터(Filter) 기반으로 동작하기 때문에 스프링 MVC와 분리되어 관리 및 동작
  • 필터(Filter) 는 Dispatcher Servlet으로 가기 전 적용 => 가장 먼저 url 요청을 받음

 

Spring Security Filter Chain 기반 인증 및 인가 처리 흐름도

 

 

 

Spring Security 인증 파트

Spring Security 인증 구조도

1. Http Request

  • 사용자가 로그인 폼에서 ID/PW 입력 → 서버로 전송

2. AuthenticationFilter (ex: UsernamePasswordAuthenticationFilter)

  • 로그인 요청을 감지해, 사용자의 ID/PW로 UsernamePasswordAuthenticationToken 객체 생성

3. AuthenticationManager

  • 인증을 시도하는 핵심 인터페이스
  • 기본 구현체: ProviderManager

4. AuthenticationProvider

  • 실제 인증 로직을 수행하는 인터페이스
  • 주로 DaoAuthenticationProvider가 사용됨

5. UserDetailsService

  • AuthenticationProvider가 이 인터페이스를 통해 사용자 정보를 불러옴
  • 백엔드 개발자가 직접 구현하는 인터페이스 (loadUserByUsername())

6. UserDetails

  • 유저 정보를 담는 객체 인터페이스
  • 직접 구현한 User 객체가 이를 implements 함
User 와 Member 차이?
스프링에서 제공하는 User & 프로젝트에서 개발자가 직접 만드는 회원 VO( ex : Member)의 차이점이 궁금하다

User : Spring Security 내부 인증/인가용, 로그인 시 UserDetails로 사용됨
Member : DB에 저장되는 사용자 데이터, 회원가입, 조회, 수정 등 도메인 역할

UserDetailsService에서 Member를 조회해서 User로 바꿔주는 역할을 함
public class CustomUserDetailsService implements UserDetailsService {
    private final UserDetailsMapper mapper;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        //mapper를 통해 Member 조회
        MemberVO vo = mapper.get(username);
        
        if (vo == null) {
            throw new UsernameNotFoundException(username + "은 없는 id입니다.");
        }
        
        //Member vo로 UserDetails return
        //업캐스팅(부모 : UserDetails, 자식 : User)
        return new CustomUser(vo);
    }
}​

7. UserDetailsService → UserDetails 반환

  • DB 등에서 사용자 조회 후 UserDetails 구현체 반환

8. AuthenticationProvider 인증 수행

  • UserDetails를 바탕으로 패스워드 검사 → 인증 성공 여부 판단

9. AuthenticationManager → Authentication 반환

  • 인증 성공 시, 인증 정보를 담은 Authentication 객체 반환

10. SecurityContextHolder 저장

  • 최종적으로 인증 정보를 SecurityContextHolder에 저장→ 이후 인증된 사용자 정보는 여기서 꺼내 씀

 


JWT(Json Web Token)

  • JSON 포맷을 이용하여 사용자에 대한 속성을 저장하는 Claim 기반의 Web 토큰
  • Header.Payload.Signature 세 가지로 구성 / 각 부분은 Base64로 인코딩, .으로 구분

 

 

 

 

Access Token

: 인증된 사용자가 특정 리소스에 접근할 때 사용되는 토큰

 

Refresh Token 

: Access Token의 갱신을 위해 사용되는 토큰

 

728x90
728x90

 

최소 공통 조상(LCA: Lowest Common Ancestor)

트리 상의 두 노드 a,b가 있을 때, 두 노드의 가장 가까운 공통 조상 노드 => 최소 공통 조상

 

6 과 5의 최소 공통 조상 구하기

 

풀이 방법

  1. 모든 노드의 부모, 깊이 기록(DFS 또는 BFS)
// DFS로 부모와 깊이 채우기
    static void dfs(int current, int d) {
        visited[current] = true;
        depth[current] = d;

        for (int next : tree[current]) {
            if (!visited[next]) {
                parent[next] = current;
                dfs(next, d + 1);
            }
        }
    }
    
    dfs(1,0); //시작 노드 1, 깊이 0

 

노드의 깊이 배열 저장

 

부모 배열 저장

 

2. 두 노드를 같은 깊이로 맞춤

while (depth[a] > depth[b])   a = parent[a];
while (depth[b] > depth[a])   b = parent[b];

 

 

 

 

 

 

3. 위로 올라가면서 공통 조상 찾기

static int lca(int a, int b) {
        // 깊이를 같게 맞춤
        while (depth[a] > depth[b]) a = parent[a];
        while (depth[b] > depth[a]) b = parent[b];

        // 공통 조상 만날 때까지 위로 이동
        while (a != b) {
            a = parent[a];
            b = parent[b];
        }

        return a;
    }

 

6 과 5 => 4와 5

 

 

전체 코드

import java.io.*;
import java.util.*;

public class Main {
    static int N, M;
    static List<Integer>[] tree;
    static int[] parent;
    static int[] depth;
    static boolean[] visited;

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        N = Integer.parseInt(br.readLine());

        tree = new ArrayList[N + 1];
        for (int i = 1; i <= N; i++) tree[i] = new ArrayList<>();

        // 간선 입력 받기
        for (int i = 0; i < N - 1; i++) {
            StringTokenizer st = new StringTokenizer(br.readLine());
            int a = Integer.parseInt(st.nextToken());
            int b = Integer.parseInt(st.nextToken());
            tree[a].add(b);
            tree[b].add(a);
        }

        parent = new int[N + 1];
        depth = new int[N + 1];
        visited = new boolean[N + 1];

        // 루트는 1번 노드
        dfs(1, 0);

        M = Integer.parseInt(br.readLine());
        StringBuilder sb = new StringBuilder();

        // 쿼리 처리
        for (int i = 0; i < M; i++) {
            StringTokenizer st = new StringTokenizer(br.readLine());
            int u = Integer.parseInt(st.nextToken());
            int v = Integer.parseInt(st.nextToken());
            sb.append(lca(u, v)).append("\n");
        }

        System.out.print(sb);
    }

    // DFS로 부모와 깊이 채우기
    static void dfs(int current, int d) {
        visited[current] = true;
        depth[current] = d;

        for (int next : tree[current]) {
            if (!visited[next]) {
                parent[next] = current;
                dfs(next, d + 1);
            }
        }
    }

    // LCA 찾기
    static int lca(int a, int b) {
        // 깊이를 같게 맞춤
        while (depth[a] > depth[b]) a = parent[a];
        while (depth[b] > depth[a]) b = parent[b];

        // 공통 조상 만날 때까지 위로 이동
        while (a != b) {
            a = parent[a];
            b = parent[b];
        }

        return a;
    }
}
728x90
728x90

 

RDBMS

관계형 데이터베이스 관리 시스템

모든 데이터를 2차원의 열과 행(테이블)의 형태로 표현

  • 수직 확장 중심(Scale-up)
  • 정형 데이터, 복잡한 관계형 모델

장점

✔ 정해진 스키마에 따라 명확한 데이터 구조 보장

✔ 각 데이터를 중복 없이 저장 -> 데이터 무결성 보장

 

 

 

NoSQL(Not Only SQL)

비관계형 데이터베이스

RDBMS가 갖고 있는 특성뿐만 아니라 다른 특성들도 부가적으로 지원

  • 수평 확장 중심(Scale-out)
  • 비정형 데이터, 빠른 읽기/쓰기, 유연한 구조

장점

✔ 스키마 X, 유연성 -> 언제든 데이터 조정과 새로운 필드 추가 가능

 

 

 

정리

RDBMS는 정형화된 데이터와 관계 중심의 모델에 강하고,
NoSQL은 유연한 구조와 대용량 처리에 최적화된 비관계형 DB

 

 

RDBMS 적합 예시

🔹 일반적인 웹 서비스 (쇼핑몰, 블로그, 예약 시스템 등)

  • RDBMS 사용 (예: MySQL, PostgreSQL)
  • 이유:
    • 고객 ↔ 주문 ↔ 상품 등 명확한 관계가 존재
    • 정확성, 무결성, 트랜잭션이 중요
    • JPA 같은 ORM과 잘 연동

 

 

NoSQL 적합 예시

🔹 실시간 채팅, 메신저, 알림 서비스

  • NoSQL 사용 (예: MongoDB)
  • 이유:
    • 메시지 형식이 다양하고 유연
    • 빠른 쓰기/읽기가 중요
    • RDBMS로 처리 시 JOIN이 많아 느려질 수 있음
728x90
728x90

 

 

문제 탐색

  • 좌측 상단 칸에 위치한 말이 알파벳 보드를
  • 상하좌우로 이동하면서
  • 최대한 몇 칸을 지날 수 있는지 출력 => 칸을 지날때마다 거리 체크
  • 새로 이동한 칸의 알파벳은 지나온 모든 칸의 알파벳과 겹치면 안된다 => 알파벳 방문 처리

 

1. 

public class Main {
    static int r, c, maxLen = 0;  //세로 길이 r, 가로 길이 c, 최대 칸 maxLen
    static char[][] arr;          //알파벳 보드 입력
    static boolean[] visited = new boolean[26];  // 알파벳 A~Z 방문 여부
    static int[] dx = {0, 0, 1, -1};
    static int[] dy = {1, -1, 0, 0};

 

2.

public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st = new StringTokenizer(br.readLine());
        r = Integer.parseInt(st.nextToken());
        c = Integer.parseInt(st.nextToken());
        arr = new char[r][c];

        for (int i = 0; i < r; i++) {           //알파벳 보드 입력
            String[] s = br.readLine().split("");
            for (int j = 0; j < c; j++) {
                arr[i][j] = s[j].charAt(0);
            }
        }
        dfs(0,0,1);  //말이 위치한다고 했던 가장 왼쪽 0,0 부터 dfs 순회 시작
        System.out.println(maxLen);
    }

 

public static void dfs(int i, int j, int depth) {
        int alpaIdx = arr[i][j] - 'A';   //알파벳을 인덱스로 방문 관리
        visited[alpaIdx] = true;         //방문처리
        maxLen = Math.max(maxLen, depth);//가장 긴 탐색 칸 수 저장

        for (int k = 0; k < 4; k++) {
            int nx = i + dx[k];
            int ny = j + dy[k];

            if(nx>=0 && nx<r && ny>=0 && ny<c) {  //나아갈 칸이 보드 규격에 속하는지
                int nextAlpaIdx = arr[nx][ny] - 'A';
                if(!visited[nextAlpaIdx]) {       //나아갈 칸의 알파벳이 아직 방문전인지
                    dfs(nx, ny, depth+1);         //칸 전진
                    visited[nextAlpaIdx] = false; //dfs 재귀가 종료되면 방문 반환
                }
            }
        }
    }

 

백트래킹

깊이 우선 탐색, 더 이상 나아갈 수 없는 상황에서 그 이전 단계로 복귀하는 과정

 

 


완전탐색 추천 문제집

https://www.acmicpc.net/workbook/view/2052

 

728x90
728x90

SOLID 원칙

좋은 객체 지향 설계를 위한 방법론 SOLID

S Single Responsibliity Principle
단일 책임 원칙
하나의 클래스는 하나의 책임만 갖는다
O Open/Closed Principle
개방-폐쇄 원칙
확장에는 열려있고, 변경에는 닫혀있다
L Liskov Substitution Principle
리스코프 치환 원칙
자식은 부모의 역할을 대체할 수 있어야 한다
I Interface Segregation Principle
인터페이스 분리 원칙
인터페이스는 작고 구체적이어야 한다
D Dependency Inversion Principle
의존 역전 원칙
구현이 아닌 추상 클래스에 의존해야 한다

 

 

S - 단일 책임 원칙 (Single Responsibility Principle, SRP)

📌 정의

  • 하나의 클래스는 오직 하나의 책임만 가져야 한다.
  • 한 클래스에 여러 책임(기능)을 넣으면 변경 이유가 많아지고, 유지보수가 어려워진다.

❌ 잘못된 예

class Report {
    void generateReport() { /* 보고서 생성 */ }
    void printReport() { /* 보고서 출력 */ }  // 출력 책임이 추가됨 ❌
}
 

✅ 개선

class ReportGenerator {
    void generateReport() { /* 보고서 생성 */ }
}

class ReportPrinter {
    void printReport() { /* 보고서 출력 */ }
}

 

 

 

 O - 개방-폐쇄 원칙 (Open/Closed Principle, OCP)

📌 정의

  • 클래스 확장에는 열려있고, 변경에는 닫혀있다.
  • 기존 코드를 변경하지 않고, 기능을 확장할 수 있어야 한다.

❌ 잘못된 예

class DiscountService {
    int getDiscount(String grade) {
        if (grade.equals("VIP")) return 20;
        else return 10;
    }
}

 

✅ 개선

interface DiscountPolicy {
    int getDiscount();
}

class VIPDiscount implements DiscountPolicy {
    public int getDiscount() { return 20; }
}

//기존 코드 변경없이 새로운 기능 추가 가능
class BasicDiscount implements DiscountPolicy {
    public int getDiscount() { return 10; }
}

class DiscountService {
    public int applyDiscount(DiscountPolicy policy) {
        return policy.getDiscount();
    }
}

 

 

 

L - 리스코프 치환 원칙 (Liskov Substitution Principle, LSP)

📌 정의

  • 자식 클래스는 언제나 부모 클래스를 대체할 수 있어야 한다.
  • 부모의 동작을 자식이 깨지 않고 유지해야 한다.

❌ 잘못된 예

class Bird {
    void fly() {}
}

class Ostrich extends Bird {
    void fly() { throw new UnsupportedOperationException(); }  // 날 수 없음 ❌
}

 

✅ 개선

interface Flyable {
    void fly();
}

class Sparrow implements Flyable {
    public void fly() { /* 날기 */ }
}

class Ostrich {
    void walk() { /* 걷기 */ }
}

 

 

 

 

I - 인터페이스 분리 원칙 (Interface Segregation Principle, ISP)

📌 정의

  • 하나의 큰 인터페이스보다는 여러 개의 작은 인터페이스가 좋다.
  • 역할을 분리하여 유연한 설계 지향

❌ 잘못된 예

interface Worker {
    void work();
    void eat();
}

class Robot implements Worker {
    public void work() {}
    public void eat() {}  // 필요없는 기능 강제 구현은 좋지않다 ❌
}

 

✅ 개선

interface Workable {
    void work();
}

interface Eatable {
    void eat();
}

class Robot implements Workable {
    public void work() {}
}

 

 

 

D - 의존 역전 원칙 (Dependency Inversion Principle, DIP)

📌 정의

  • 구체적인 클래스 구현체에 의존하지 않고, 공통된 추상화(인터페이스)에 의존

❌ 잘못된 예

class MySQLDatabase {
    void save() {}
}

class OrderService {
    MySQLDatabase db = new MySQLDatabase();
    void placeOrder() {
        db.save();
    }
}

 

✅ 개선

interface Database {
    void save();
}

class MySQLDatabase implements Database {
    public void save() {}
}

class OrderService {
    Database db;
    OrderService(Database db) {  //OrderService는 인터페이스인 Database에 의존
        this.db = db;            //db를 다른 걸로 바꿔도 OrderService에는 수정x
    }
    void placeOrder() {
        db.save();
    }
}

 

 

 

 


제어의 역전(IoC)

📌 개념

  • 일반적으로 객체는 스스로 필요한 의존 객체를 생성하거나 찾아서 사용
  • IoC는 이 흐름을 "역전 "시켜서, 객체가 직접 의존 대상을 만들지 않고 외부에서 주입받도록 하는 개념

🎯 핵심 의미

"객체가 제어권을 갖는 게 아니라, 제어권을 외부(프레임워크나 컨테이너)에 넘긴다"

 

 

❌ IoC 적용 전: 직접 생성

class OrderService {
    private final PaymentService paymentService = new PaymentService(); // 직접 생성
}

 

✅ IoC 적용 후: 외부에서 주입

class OrderService {
    private final PaymentService paymentService;

    public OrderService(PaymentService paymentService) {  // 생성자 주입
        this.paymentService = paymentService;
    }
}

 

의존성 주입(DI)

 

📌 개념

  • IoC를 구현하는 구체적인 방법으로, 객체가 필요한 의존 객체를 외부에서 전달받는 방식

 

💡 DI 3가지 방법

생성자 주입 가장 권장되는 방식. final 필드와 함께 사용 가능
세터 주입 필드를 setXXX() 메서드로 주입
필드 주입 @Autowired로 바로 주입 (Spring 한정, 테스트 불리)

 

 

✅ 생성자 주입 예 (스프링 방식)

* @Autowired는 Spring 4.3 이후 생성자 1개면 생략 가능

@Service
public class OrderService {
    private final PaymentService paymentService;

    @Autowired
    public OrderService(PaymentService paymentService) {  //생성자 주입
        this.paymentService = paymentService;
    }
}

 

 

✅ 세터 주입 예 (스프링 방식)

@Service
public class OrderService {

    private PaymentService paymentService;

    @Autowired
    public void setPaymentService(PaymentService paymentService) {  //세터주입
        this.paymentService = paymentService;
    }
}

 

✅ 필드 주입 예 (스프링 방식)

@Service
public class OrderService {

    @Autowired
    private PaymentService paymentService;  //필드 주입

}

 

728x90

+ Recent posts