소개
구멍가게코딩단은 2015년부터 개발과 관련된 직종의 사람들의 모임을 위해 만들어진 커뮤니티입니다.
강의의 진행은 현재 구멍가게 코딩단을 운영하고 있는 쿠키 매니저가 진행하고 있습니다.
2000년부터 개발을 시작하였고 벤처 기업, 중소 기업, 대기업 / 프리랜서 등의 개발 경험을 가지고 있습니다.
2010년대 부터는 주로 기업체 강의와 컨설팅을 주요 업무로 하고 있습니다.
주요 저서
코드로 배우는 스프링 웹 프로젝트
코드로 배우는 스프링 부트
코드로 배우는 React
RESTful API 서버 구현
스프링 6 레거시 프로젝트
자바 웹 개발 워크북
React Native 인 액션(번역)
개발/강의
삼성 SDS 개발팀 근무
SK 고객행복 주식회사 근무
KT / SK / 포스데이터 / 교보 생명 / 신세계 / 아시아나 / 건강보험 관리공단 등등 기업체 강의
강의
수강평
- Beginning Kubernates-Based MSA (with Spring Boot) - Part 1
- 코드로 배우는 React 19 with 스프링부트 API서버
- Beginning Kubernates-Based MSA (with Spring Boot) - Part 1
게시글
질문&답변
장바구니 질문
가입한 회원중에 실제로 구매의향이 있는 회원을 분리하기 위해서 였습니다. 10명이 가입해도 실제 장바구니에 물건을 담는 회원은 적기 때문에 장바구니쪽 데이터를 좀 줄이는게 좋다고 생각합니다. 반대로 한번이라도 장바구니에 물건을 담은 회원은 뭔가 구매의사가 있었던 회원이라고 판단하고 남겨두었습니다만 말씀하신대로 삭제를 해도 크게 문제가 되지는 않을듯 합니다. 장바구니에 물건을 담은 회원들은 나중에 DM등을 발송할 대상이 되기도 한다고 생각하고 봐 주시면 좋을듯 합니다.
- 좋아요수
- 0
- 댓글수
- 1
- 조회수
- 19
질문&답변
리프레시토큰
Access Token(AT)과 Refresh Token(RT)APIRefreshController에서 Access Token의 갱신 작업을 하고 있는데요.AT가 입장권, RT가 교환권이므로 교환권이 만료되었다는 얘기는 결국 새로 로그인해야 한다는 의미가 됩니다. 질문하신 내용이 RT이 만료된 상황에 대한 것이라면 -- 새로 로그인을 유도하는게 맞습니다. 서버에서는 RT를 확인하는 부분입니다. Map claims = JWTUtil.validateToken(refreshToken); 여기서 JWTUtil에 있는 validateToken( )은 예외를 발생시킵니다. public static Map validateToken(String token) { Map claim = null; try{ SecretKey key = Keys.hmacShaKeyFor(JWTUtil.key.getBytes("UTF-8")); claim = Jwts.parserBuilder() .setSigningKey(key) .build() .parseClaimsJws(token) // 파싱 및 검증, 실패 시 에러 .getBody(); }catch(MalformedJwtException malformedJwtException){ throw new CustomJWTException("MalFormed"); }catch(ExpiredJwtException expiredJwtException){ throw new CustomJWTException("Expired"); }catch(InvalidClaimException invalidClaimException){ throw new CustomJWTException("Invalid"); }catch(JwtException jwtException){ throw new CustomJWTException("JWTError"); }catch(Exception e){ throw new CustomJWTException("Error"); } return claim; }만료되거나 잘못된 RT에 대해서 알 수 있고 이를 advice에서 처리합니다. @ExceptionHandler(CustomJWTException.class) protected ResponseEntity handleJWTException(CustomJWTException e) { String msg = e.getMessage(); return ResponseEntity.ok().body(Map.of("error", msg)); }이렇게 되면 RT가 만료되었을때 사용자는 {error:에러메시지}를 받게 됩니다. RT를 새로 발행할 때는 RT의 남은 시간을 봐서 새로운 RT도 만들어 줍니다. String newRefreshToken = checkTime((Integer)claims.get("exp")) == true? JWTUtil.generateToken(claims, 60*24) : refreshToken; 이렇게 하면 사용자는 주기적으로 AT와 RT가 변경되게 됩니다. 최근에는 AT 갱신시에 RT를 무조건 새로 만들어주는 방식도 많이 사용하고 있습니다. React쪽(다음챕터) 커스텀 훅으로 에러가 발생하면 경고창을 보여주도록 구성합니다.
- 좋아요수
- 0
- 댓글수
- 1
- 조회수
- 53
질문&답변
로그인 확인
커스텀 훅으로 빼기 전에 {loginStatus === 'pending' && className="bg-amber-300">로그인 중...} {loginStatus === 'fulfilled' && className="bg-green-300">로그인 성공!} {loginStatus === 'rejected' && className="bg-red-300">로그인 실패}부분 때문에 그렇게 생각하실 수 있습니다.변경된 커스텀 훅을 보면 변수의 이름은 같지만 내용물은 조금 달라집니다. //로그인 상태 객체 const loginState = useSelector((state: RootState) => state.loginSlice) //로그인 여부 const loginStatus = loginState.status처럼 loginStatus는 true/false가 되고 loginState는 로그인한 사용자의 정보입니다(그래서 loginState.nickname처럼 사용하게 됩니다.변수명이 동일해서 그렇게 느끼실 수도 있었겠네요..혹시 몰라서 AI의 설명을 첨부합니다.loginStatus === 'fulfilled'처럼 사용하실 필요가 없습니다. 지금 작성하신 코드처럼 {loginStatus && ...} 형태로 사용하시는 것이 맞습니다.그 이유와 두 개념의 차이를 명확히 정리해 드릴게요.1. fulfilled는 컴포넌트가 아니라 Redux 내부에서 쓰는 개념입니다loginPostAsync.fulfilled는 Redux Toolkit의 createAsyncThunk가 API 요청에 성공했을 때 발생하는 액션 상태(Lifecycle)입니다.이는 컴포넌트가 아니라 loginSlice.ts 내부의 extraReducers에서 "로그인 API 통신이 성공했으니 스토어의 상태를 이렇게 바꿔라"라고 지정할 때 사용합니다.2. 컴포넌트는 이미 처리된 '결과'만 가져다 씁니다컴포넌트는 비동기 통신의 진행 단계를 직접 판별하는 곳이 아닙니다. 이미 비동기 통신이 성공하여 Redux 스토어에 저장된 최종 결과값(loginStatus)을 가져와서 화면에 보여주는 역할만 합니다.따라서 loginStatus가 이미 true 또는 유효한 값으로 세팅되어 있기 때문에, 조건문에서는 단순히 존재 여부나 참/거짓만 판별하면 됩니다.3. 만약 === 'fulfilled'를 쓰면 어떻게 될까요?loginStatus가 불리언(true/false) 타입이나 객체라면 === 'fulfilled'라는 비교문은 항상 거짓(false)이 되어 메뉴가 아예 나타나지 않는 버그가 발생합니다.loginStatus는 오직 로그인 상태 여부만을 깔끔하게 대변하도록 설계되었기 때문에, 지금처럼 {loginStatus && ...} 또는 {loginStatus ? (...) : null} 형태로 쓰는 것이 가장 올바른 리액트/리덕스 패턴입니다.현재 작성하신 커스텀 훅과 컴포넌트 구조는 아주 정상적이니 이대로 사용하셔도 무방합니다
- 좋아요수
- 0
- 댓글수
- 3
- 조회수
- 70
질문&답변
지금 강의가 어려우신분 추천드립니다.
좋은 의견 감사합니다. 스프링이 부족하신 분들을 위해 스프링 강의도 만들어두긴 했습니다. 동영상이 편하신분들도 있으실 겁니다.
- 좋아요수
- 0
- 댓글수
- 1
- 조회수
- 74
질문&답변
기존의 완성된 CRUD의 Build.gradle에 security관련 dependency를 추가하면 문제가 생깁니다.
소스 코드를 확인할수 있도록 github 주소나 코드를 압축해서 cookie_00@naver.com으로 보내주세요 살펴보겠습니다
- 좋아요수
- 0
- 댓글수
- 2
- 조회수
- 75
질문&답변
CRA ? 질문입니다
열심히 들어주셔서 감사합니다 CRA는 create react app을 줄여서 쓴 약어입니다. 리액트 생성방식이 여러가지 있어서 CRA방식이라고 구분하고 있습니다. 나중에 또 다른 질문이 생기시면 남겨주세요
- 좋아요수
- 0
- 댓글수
- 1
- 조회수
- 67
질문&답변
음성불량
헉.. 음성이 안 나오는 부분이 있었나요?제가 그런 피드백을 받은 적은 없어서 모르고 있었습니다.학습에 불편을 드려 죄송합니다. 혹시 어떤 강의 인지 알려주시면 최대한 빠르게 조치하겠습니다.현재 질문에 있는 '쿠키를 이용한 로그인'강의는 제가 다시 확인해 봤는데 이상이 없었습니다. 다른 강의일까요?
- 좋아요수
- 0
- 댓글수
- 1
- 조회수
- 65
질문&답변
개인 git에 코드를 올려도 될지 문의드립니다.
학습 내용을 공부하시면서 정리하신 것을 공개하시는 건 괜찮습니다. 다만 가끔은 출처를 언급해 주시면 감사하겠습니다. 그리고 이건 제 개인적인 생각인데 소스 코드가 있어도 순서를 모르면 봐도 알기 어렵지 않을까요?^^;
- 좋아요수
- 0
- 댓글수
- 2
- 조회수
- 88
질문&답변
mybatis 를 이용한 crud에서 insert에서 resultType='long" 이라고 하셨는데요,
6:42초 부분을 말씀하시는거 같습니다select last_insert_id( ) 의 결과는 가장 최근에 추가된 새로운 번호값을 숫자로 반환합니다. MyBatis에서는 select를 하면 resultType을 지정한다고 생각하시면 되는데.. 숫자의 경우는 int 보다 큰 long 타입으로 처리하는 것이 안전하기 때문에 시퀀스나 auto_increment의 결과는 long으로 처리합니다. 혹시 제가 질문 의도를 잘못 이해할수도 있으니 현재 학습중인 부분을 알려주시면 좀 더 자세히 설명할 수 있을 듯 합니다.
- 좋아요수
- 0
- 댓글수
- 1
- 조회수
- 68
질문&답변
깃허브주소 궁금합니다.
강의 관련된 소스 코드는 대부분의 섹션의 마지막에 수업자료에 첨부되어 있고 GitHub같이 외부에 공개되지 않으니 양해 부탁드립니다.
- 좋아요수
- 0
- 댓글수
- 2
- 조회수
- 86





