시애틀의 명물인 스타벅스 1호점! 평소엔 사람이 줄을 서서 기다린다고 하지만 평일 낮과 평일 밤에 방문한 덕분인지 줄을 오래기다리지 않았다ㅎㅎ

 

 

 

 

 

- TIL -

구현 목표 )

JWT 내부의 payload 에 Role 값을 key, value 로 지정한 후 클라이언트에서 해당 JWT 를 가져와 서버로 검증할때 해당 유저의 정보를 가져와 DB 에서 값을 추출하여 접근 api 주소에 권한이 있는지 확인하여 처리하도록 구현하고자 하였다.

 

 

 

문제 발생 )

1. 우선 현재 프로젝트에서 관리하는 Role 값은 enum 타입으로 설정하여 관리중이고 Spring Security 에서는 GrantedAuthority 객체나 SimpleGrantedAuthority 객체를 사용하여 객체 값을 관리하므로 관리하는 객체 타입이 다른 점에서 발생할 수 있는 문제가 있다고 판단하였다.

 

2. 토큰이 정상정으로 발급됨을 확인한 뒤 HttpSecurity 클래스의 hasRole("USER") 메소드나 @PreAuthrize("hasAnyRole('USER')") 어노테이션을 사용하여 분명 권한 정보를 토큰에서 추출하여 비교한 뒤 검증해야하는데 모든 요청의 응답이 403 Forbidden 로 처리되는 문제가 발생했다.

 

 

 

문제 해결 )

1. Role 값의 타입 문제는 토큰을 구현하며 공식문서와 인터넷 서핑을 통해 찾아보니 어짜피 JWT 에 담기는 모든 내용은 문자열로 변환하여 저장되고 검증되므로 토큰을 구현하는데 있어서 현재 사용하는 클래스 타입에서 캐스팅을 통해 문자열로 변환한 뒤 저장해주면 된다는 해결책을 찾게 되었다.

UsernamePasswordAuthenticationFilter 클래스의 successfulAuthentication 메소드를 오버라이딩하여 payload 부분에 내가 넣고자하는 정보를 넣어서 저장, 이때 user.getRole() 메소드를 통해 가져온 값은 enum 타입이므로 name() 메소드를 사용하여 String 타입으로 캐스팅하여 토큰을 빌드하였다

 

 

2. 가장 큰 문제점이었던 권한이 정상적으로 검증되지 않았던 문제는 우선적으로 토큰에 정상적인 권한 값이 들어가있는지 토큰의 정보를 추출하여 확인해보아도 내가 설정한 USER 값이 정상적으로 출력되는 것을 확인할 수 있었다. 그래서 공식문서를 찾아보니 hasRole 을 사용하여 권한을 검증하면 ROLE_ 의 접두가사 권한의 앞에 붙는다는 사실을 알게되었고 유저의 Role 값을 검증함에 있어서 앞에 접두사를 붙여서 코드를 구현하니 정상적으로 200 OK 응답이 돌아오는 것을 확인하였다.

UserDetails 클래스에서 getAuthorities 메소드를 오버라이딩하여 role 값을 추출하여 앞에 "ROLE_" 접두사를 붙여서 검증하게끔 설정하니 200 Ok 사인을 반환

728x90

 

 

 

 

 

시애틀에 있는 Amazon spheres...!!! 아마존에 개발 부서에 종사하는 지인분이 계셔서 덕분에 내부에 들어가서 구경할 수 있었다.

 

 

 

 

 

- TIL -

구현 목표 ) Spring Security 와 JWT(Json Web Token) 을 활용하여 로그인 시 토큰을 발행하고 프론트에서 넘어오는 요청을 토큰의 내부 payload 값을 확인하여 처리하는 로직을 구현하고자 하였다.

 

 

 

문제 발생 ) Spring Security 의 인가 구현 방식은 UsernamePasswordAuthenticationFilter 클래스가 form-data 형식의 데이터를 받아 처리하게끔 구현되어 있다, 하지만 현재 구현하고 있는 프로젝트의 경우 요청을 form-data 형식이 아닌 Json 데이터로 처리하기로 규약하였고 이를 위해 해당 클래스를 상속받아 커스텀 처리를 진행할 필요가 있었다.

 

 

 

문제 해결 ) UsernamePasswordAuthenticationFilter 클래스에서 로그인 정보를 처리하는 obtainEmailAndPassword 메소드를 선언하여 HttpServletRequest 로 요청된 form-data 의 이메일, 비밀번호가 담긴 로그인 정보를 json 형태로 파싱하여 오버라이딩할 attemptAuthentication 메소드에 로그인 정보를 DTO 형태로 전달하여 사용하게끔 코드를 작성하였다.

obtainEmailAndPassword 메소드 (form-data 를 json 형태로 파싱하여 DTO 에 담아 데이터를 반환)
파싱된 로그인 정보를 DTO 를 사용하여 전달받아 회원 정보를 검증할 수 있게끔 처리

 

 

 

보완점) 기본적으로 Spring Security 에서 로그인을 검증할때 사용되는 방식이 form-data 방식이다보니 추가적으로 데이터 검증이 필요하거나 토큰을 발급하는 데 있어서 많은 클래스 커스텀이 필요할 것으로 보인다, 또한 기존의 Spring Security 는 username 과 password 라는 변수 명으로 로그인 처리를 진행하게 되는데 현재 구현하고자 하는 프로젝트에서는 email 과 password 값으로 로그인을 진행할 것이므로 email 값을 받아 DB 와 검증 절차를 거치게 하는 로직의 추가 구현이 필요하다.

728x90

 

 

 

 

 

역시 미국은 총기의 나라였다... 권총부터 샷건까지 사격해볼 수 있는 기회가 또 언제 있으려나ㅎㅎ

 

 

 

 

 

- TIL -

1. Domain 설계 시 테이블 명 이슈

( User 테이블을 JPA 를 통해 설계하고 실행시키는 순간 User 테이블을 생성하는 쿼리에서 Syntax error 가 발생한 것을 확인 )

 

Caused by: org.h2.jdbc.JdbcSQLSyntaxErrorException: Syntax error in SQL statement "create table [*]user (id bigint not null, email varchar(255) not null, gender enum ('FEMALE','MALE'), nickname varchar(255) not null, password varchar(255) not null, role varchar(255) not null, primary key (id))"; expected "identifier"; SQL statement:

 

> 테이블 생성 코드 자체에는 잘못된 부분이 없는 것을 확인, 해당 문제를 해결하기 위해 구선생님께 "jpa 테이블 생성 실패", "jpa JdbcSQLSyntaxErrorException" 등을 검색해보다가 한 가지 사실을 알게되었다...!

 

 

2. SQL 표준에는 USER 가 예약어로 설정되어 있다.

( SQL 쿼리에는 USER 라는 예약어가 설정되어 있으며, SELECT USER() 와 같은 쿼리를 실행시키면 현재 접속중인 사용자의 이름을 반환하게 된다 )

 

> Oracle, MySQL 등의 쿼리에서는 예약어를 유연하게 처리할 수 있게끔 설정되어 있으나 H2 Database, postgresql, ms-sql 등 데이터베이스에서는 예약어에 대한 처리과정이 더 엄격하게 설정되어 있어 USER 라는 테이블을 생성하지 못한다고 한다.

 

 

3. 테이블 명을 USERS, MEMBER 등으로 변경하여 생성하기

 

Hibernate: create table users (id bigint not null, email varchar(255) not null, gender enum ('FEMALE','MALE'), nickname varchar(255) not null, password varchar(255) not null, role varchar(255) not null, primary key (id))

테이블 명을 USERS 로 변경한 뒤 코드를 실행 / 테이블이 정상적으로 생성된 것 확인

728x90

 

 

 

 

 

미국에서 산책시킨 귀여운 멍멍이... 할 줄 아는 개인기가 하나도 없었지만 귀여웠다

 

 

미국 여행을 1달 넘게 다녀오면서 개발에 소홀해진 느낌이 들어 오늘부터 TIL(Today I Learned) 포스트를 기재해보려고 한다.

( 포트폴리오와 이력서를 작성하느라 미국 여행도 정신이 없이 지나가버린 듯 하다.... )

 

국비교육 과정에서 함께 프로젝트를 한번 진행했던 팀원분과 같이 사이드 프로젝트로 랜덤 채팅을 구현하고 서비스해보기로 했다.

 

기본적으로 Login 기능과 Register 기능은 필수로 구현하기로 하였고 우선적으로 구현할 부분은 랜덤한 상대와 텍스트 채팅을 먼저 구현하고 텍스트 채팅 구현이 완료되면 영상통화도 같이 구현해보기로 하였다.

 

 

 

 

 

- TIL -

 

1. 처음 Spring 프로젝트를 생성하고 빌드

( 프로젝트를 구현하기 위해 추가된 의존성 : Lombok, Spring Web, Spring Security, Spring Data JPA, MySQL Driver, Spring Boot DevTools, H2 Database )

 

2. H2 Database 를 in-memory 를 사용하게끔 설정하여 주 메모리에 Database 를 활성화 하여 사용하고자 하였고 좌측 같이 설정 후 실행하여 H2 콘솔 주소(http://localhost:8080/h2-console) 로 접근하니 우측과 같이 Spring Security 에 설정된 Username / Password 를 요구하는 창이 출력되었다

( 나는 Security 에서 제공하는 폼 로그인 기능을 사용하지 않을 것이지만... 혹시 모르니 해당 주소만 인가를 얻을 수 있는 방법을 검색해보았다 )

application.yml 의 database 설정 / H2 콘솔 주소 접속 시 출력물

 

3. 검색을 통해 알아낸 바에 의하면 Spring Security 는 자동으로 SecurityFilterChain 빈을 찾아 해당 애플리케이션 요청에 대해 보안 필터 체인을 적용한다고 하는데 나는 이 SecurityFilterChain 부분을 구현하는 방법을 찾아 기본적인 폼 로그인 방법을 사용하지 않게끔 설정하기로 하였다.

jwt 를 사용하여 기본적인 인가 부분을 모두 설정할 것이기 때문에 formLogin 과 httpBasic 을 비활성화하였고, Session 과 Cookie 를 활용하여 인증을 구현할때 발생할 수 있는 csrf 보호 기능을 비활성화 하였다. 이후 localhost:8080/h2-console 하위 모든 경로의 요청을 인가가 필요없이 허용하도록 설정하였다.

 

4. 돌아왔구나 H2 태식이....

익숙한 실루엣의 H2 console 로그인 화면이 정상적으로 출력되는 것을 확인!

 

 

 

- 의문점 및 보완점 -

1. Restful API 서버를 구축하는데 있어서 인가 부분을 오롯이 토큰에만 의존하여 설계해도 되는지 의문이 생긴다

( 예를 들어, 토큰에 role 값을 payload 에 담아서 사용한다고 가정했을때 이 권한 부분이 변조될 위험성은 없는가? )

728x90
728x90

 

 

 

 

 

1. homebrew 를 사용하여 mysql 을 설치

brew install mysql

 

 

2. 설치된 mysql 의 버전 확인

mysql 버전을 확인하여 정상적으로 설치되었는지 확인 ( mysql --version )

 

 

3. mysql 을 활성화

설치된 mysql 을 brew 를 사용하여 활성화 시킨다 ( brew services list 를 통해 활성화된 서비스 확인 > brew servies start mysql 를 통해 서비스 활성화 )

 

 

4. mysql 접속

설치된 mysql 의 root 계정으로 접속 ( 최초 비밀번호 세팅을 하지 않았기 때문에 비밀번호 입력창에서 엔터만 입력 )

 

 

5. mysql 테이블 확인 및 생성

show databases 를 통해 현재 생성된 데이터베이스를 확인 후 create database 를 통해 DB 생성 후 다시 확인

 

 

6. mysql root 계정 비밀번호 설정 및 확인

ALTER USER 'root'@'localhost' IDENTIFIED BY '{변경할 비밀번호}'; 를 사용하여 root 계정의 비밀번호를 변경한 뒤 exit 으로 mysql 입력을 종료하고 다시 root 권한으로 로그인 시도하여 비밀번호가 변경된 것을 확인!

 

 

7. mysql 사용자 계정 생성 및 DB 접근 권한 부여

CREATE USER '{USER 이름}'@'localhost' IDENTIFIED BY '{비밀번호}'; 를 사용하여 user 계정을 생성한 뒤 GRANT ALL PRIVILEGES ON {권한을 부여할 DB 이름}.* TO '{권한을 부여받을 USER}'@'localhost'; 를 통해 생성된 muzinut 이라는 DB 에 muzinut USER 에게 모든 권한을 부여

 

 

8. 기존에 세팅되어 있던 H2 database 에서 MySQL 로 application.yml 설정 바꾸기

기존에 설정되어 있던 개발용 H2 데이터베이스에서 새로 설정한 mysql 데이터베이스로 설정 변경

 

 

9. 생성된 테이블 확인

USE {DB명}; 을 사용하여 사용할 DB 를 선택한 뒤 show tables; 명령어를 사용하여 생성된 테이블을 확인

 

728x90

 

 

GitHub 에서 제공하는 pages 기능을 사용하여 리액트 프로젝트를 배포하기

리액트를 사용하여 포트폴리오를 작성하고 작성된 포트폴리오를 GitHub 에 배포하고자 한다.

 

배포 과정을 간단히 설명하자면 아래와 같다.

------------------------------------------------------------------------------

1. React 프로젝트를 생성

2. gh-pages 패키지 설치

3. package.json 폴더 내에 홈페이지 주소 추가하기 및 predeploy, deploy 추가하기

4. GitHub Repository 생성하기

5. 등록한 Repository 를 원격지로 등록하기 ( add, commit, push )

6. GitHub pages 에 배포하기

7. 배포된 화면 확인하기

------------------------------------------------------------------------------

 

그럼 GitHub Pages 를 사용하여 배포하는 과정을 자세하게 살펴보겠다.

 

 

 

1. React 프로젝트를 생성

npx create-react-app {프로젝트명} : react 프로젝트를 생성

 

 

 

2. gh-pages 패키지 설치

우선 gh-pages 패키지를 설치하기 위해서 생성된 React 프로젝트 디렉토리로 이동하여 진행한다

생성된 프로젝트로 이동 > npm install --save gh-pages 명령어를 사용하여 gh-pages 패키지를 설치

 

 

 

3. package.json 폴더 내에 홈페이지 주소 추가하기 및 predeploy, deploy 추가하기

package.json 파일을 열어 homepage 항목을 추가한 뒤 값은 http://{사용자이름}.github.io/{생성될 저장소 이름} 으로 지정하고 predeploy 항목과 deploy 항목을 추가해준다

 

 

 

4. GitHub Repository 생성하기

아까 package.json 에서 작성한 저장소 이름으로 레포지토리를 생성해준다.

 

 

 

5. 등록한 Repository 를 원격지로 등록하기

생성된 레포지토리를 원격지로 등록 및 프로젝트를 add, commit, push 를 진행

 

 

 

6. GitHub pages 에 배포하기

설치한 gh-pages 를 사용하여 배포 ( npm run deploy - package.json 에서 설정한 deploy 라는 별칭의 명령어를 실행 )

 

 

 

7. 배포된 화면 확인하기

레포지토리 주소로 접속하게 되면 정상적으로 배포된 화면이 띄워지는 것을 확인할 수 있다.

 

 

 


 

 

 

수정 배포하기

프로젝트를 수정 > npm run deploy 실행 > 배포된 페이지 확인

 

수정 배포 테스트를 위해 App.js 파일 수정

 

 

 

npm run deploy 로 배포 실행

 

 

 

레포지토리의 Actions 탭에서 배포된 항목에 초록불이 들어와 있다면 배포 성공!

 

 

 

배포된 사이트를 통해 확인!

 

 

 


728x90

 

728x90

 

 

React Component 란?

React 컴포넌트는 페이지에 렌더링할 React 엘리먼트를 반환하는 작고 재사용 가능한 코드 조각입니다. 가장 간단한 React 컴포넌트는 React 엘리먼트를 반환하는 일반 JavaScript 함수

( 함수 뿐만 아니라 class 로도 컴포넌트를 작성할 수 있다 )

 

Component 규약

1. 컴포넌트 함수의 첫 글자는 대문자로 시작한다.

컴포넌트 변수명은 대문자로 시작

 

2. 컴포넌트의 리턴 값은 최상단의 하나의 태그만을 갖는다

병렬의 태그를 리턴할 수 없다

 

 

컴포넌트를 사용하면 HTML 요소들을 더욱 편리하게 관리할 수 있으며 useState 와 useEffet 를 사용하여 동적인 화면을 구성할 수 있다.

728x90

'FE > React' 카테고리의 다른 글

[React] useState  (0) 2024.07.19

 

 

프로젝트를 진행하면서 백엔드 서버에서 프론트 측으로 이미지 정보를 전송해줘야 하는 경우가 많이 발생하였다.

 

보통은 이미지 서버를 별도로 운영하여 원본의 데이터를 제공하지 않고 이미지 서버(Storage)에 저장된 이미지의 url 을 제공하여 이미지를 제공하지만 제한된 프로젝트 기한 내에 이미지 서버까지 구축하는 데는 시간적으로 촉박할거 같아 백엔드 서버에서 원본 데이터를 프론트로 전송해주기로 했다.

 

처음엔 프론트에서 이미지나 파일을 multipart/form-data 로 백엔드 서버로 전송하여 처리했기 때문에 동일하게 백엔드 서버도 multipart/form-data 를 사용하여 이미지를 프론트로 전송하면 된다고 생각했다.

 

그러나 multipart/form-data 로 이미지를 전송된 이미지를 프론트에서 처리할 수 있는 방법이 존재하는지 몰랐고, 여러가지 방법을 찾던 와중 Base64 형태로 이미지를 백엔드 측에서 인코딩하여 문자열로 프론트로 보내주게 되면 프론트에서는 문자열로 인코딩된 이미지 정보를 <img src="data:image/<이미지확장자>;base64,<data코드>"> 다음과 같은 형태로 인코딩된 이미지를 바로 출력할 수 있다는걸 알게 되었다.

 

결국, 서버에서 이미지를 전송할때 Base64 로 이미지를 인코딩하여 인코딩된 문자열을 전송해주기로 하였다.

DTO 의 이미지 파일 이름을 기준으로 파일을 불러와 인코딩하여 다시 DTO 에 저장 후 RestController 를 통해 응답

 

위와 같이 Base64 로 인코딩된 이미지를 바로 보낼 때 장점과 단점

 

- 장점 -

1. 서버에서 이미지를 저장할 때 이미지의 원본을 저장할 필요 없이 인코딩된 문자열을 저장하여 바로 문자열을 불러 응답해줄 수 있다

2. 렌더링될 때 HTML dom 을 통해 렌더링되기 때문에 이미지가 끊기지 않고 출력된다

 

- 단점 -

1. 프론트 코드의 길이가 매우 길어지므로 코드의 가독성이 떨어진다

2. 용량이 증가한다

( 256가지를 표현할 수 있는 바이트를 printable한 64가지를 사용해서 표현하니 당연하다
다시 말해, 8비트를 6비트로 표현하는 것이다
3개의 8비트는 4개의 6비트로 표현할 수 있다
따라서 Base64 인코딩을 사용하면 원본보다 33%의 크기 증가가 발생한다 )

 

( 현재는 이미지 원본을 보내서 useState 에 데이터 키 값을 통해 받은 이미지 값을 return 하는 함수를 생성하고 해당 State 를 이미지 src 부분에 사용하면 이미지가 출력되는 것을 알게되었으므로 추후에 서버측과 클라이언트 측의 이미지 처리 방식 코드의 변경이 필요하다 )

 


 

- 참고 블로그 -

 

Base 64와 base64 img 사용하기

html에 img의 src에 url이 아닌 숫자와 문자로 구성된 긴 코드가 들어간 경우가 있었는데 data:image/png;base64와 같은 형태였다.검색 후 아래와 경우에는 이미지를 base64인코딩 방식으로 사용한다는것을

velog.io

 

728x90

+ Recent posts