반응형

문제

kopring 프로젝트를 작성하던 중 POST method에서 body가 x-www-form-urlencoded 형태로 들어오는 요청을 처리하는 로직을 작성하고 있었다.

간단한 return을 만들고 통신 확인을 해보니 다음과 같은 오류가 발생하였다.

 

💣 spring boot 콘솔 에러

2024-07-31T09:44:19.505+09:00  WARN 48404 --- [gbike-tagride-api-server] [nio-8080-exec-1] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.web.HttpMediaTypeNotSupportedException: Content-Type 'application/x-www-form-urlencoded;charset=UTF-8' is not supported]

 

💣 postman 응답 에러

{
    "timestamp": "2024-07-31T00:44:19.509+00:00",
    "status": 415,
    "error": "Unsupported Media Type",
    "path": "/v2/vehicle/unlock"
}

 

그리고 내가 작성했던 controller는 아래와 같다.

@PostMapping(UNLOCK)
fun unlock(
    @RequestBody unlockRequestDto: UnlockRequestDto,
): String {
    return "unlock"
}

 

문제 해결 시도

1. 컨버터 추가

Spring MVC 설정에 FormHttpMessageConverter를 추가하여 application/x-www-form-urlencoded 타입을 처리할 수 있도록 한다.

@Configuration
class WebConfig : WebMvcConfigurer {
    override fun configureMessageConverters(converters: MutableList<HttpMessageConverter<*>>) {
        converters.add(FormHttpMessageConverter())
    }
}

 

결과 : 안됨!! 동일한 오류 발생

2. PostMapping에 consume 속성을 추가하여 type을 처리할수 있도록 명시해주기

컨트롤러 메서드에 @RequestMapping 또는 @PostMapping 어노테이션에 consumes 속성을 추가하여 application/x-www-form-urlencoded 타입을 지원하도록 설정한다.

@PostMapping(UNLOCK, consumes = ["application/x-www-form-urlencoded"])
fun unlock(
    @RequestBody unlockRequestDto: UnlockRequestDto,
): String {
    return "unlock"
}

 

결과 : 안됨 !! 동일한 오류 발생

 

3. @RequestParam을 사용한다.

(안될거 알았다.. 애초에 넘기는게 body 였다.)

@PostMapping(UNLOCK)
fun unlock(
    @RequestParam unlockRequestDto: UnlockRequestDto,
): String {
    return "unlock"
}

 

결과 : 안됨 !! 새로운 오류 발생

💣 spring boot 콘솔 에러

2024-07-31T09:56:06.532+09:00  WARN 49097 --- [gbike-tagride-api-server] [nio-8080-exec-1] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.web.bind.MissingServletRequestParameterException: Required request parameter 'unlockRequestDto' for method parameter type UnlockRequestDto is not present]

💣 postman 응답 에러

{
    "timestamp": "2024-07-31T00:56:06.535+00:00",
    "status": 400,
    "error": "Bad Request",
    "path": "/v2/vehicle/unlock"
}

 

google 선생님의 도움으로 이것저것 검색해 보니 다음과 같은 답을 찾아냈다.

@RequestBody는 body로 받은 JSON을 jackson을 통해 Object로 변경하는데 form은 Content-Type: application/x-www-form-urlencoded 으로 전송하기 때문에 타입이 맞이 않아 에러가 발생한다.

 

문제 해결

따라서 RequestBody를 제거해 주면 된다!

@PostMapping(UNLOCK)
fun unlock(unlockRequestDto: UnlockRequestDto): String
{
    return "unlock"
}

 

결과 : 성공!

unlock
반응형
반응형

지난날 신규 프로젝트를 진행하면서 도메인 특성 상 많은 소셜미디어 플랫폼을 연동해야했다.

네이버 블로그와 인스타그램, 유투브를 타겟으로 하여 유저의 미디어 인사이트를 조회하고자 연동을 진행하였고,

이후 간편로그인 도입을 위해 카카오 연동도 추가로 진행하였다. 가장 연동 난이도가 낮았던 네이버를 예로 기록해보았다.

참고로 가장 어려웠던건 페이스북-인스타그램 이었다..............

 

# 소셜 로그인?

대부분의 소셜미디어에서 OAuth 인증을 통한 Third Party 앱의 인증 프로세스를 제공하고 있다. 각종 developer 사이트에 접속하면 이를 이용할수 있는 방법이 안내되어 있다.

 

[1] 개발자 등록하기

먼저 개발자센터를 이용하기 위해선 개발자로 등록해야 한다.

인증을 마치면 바로 애플리케이션 등록화면으로 넘어간다.

 

[2] 네이버 소셜로그인을 이용할 어플리케이션 등록하기

네이버 OAuth인증을 사용할 나의 어플리케이션을 등록한다.

참고로 애플리케이션은 여러개 등록이 가능하다. 아래 화면은 개발자로 기 등록된 상태에서 애플리케이션을 추가로 등록할때의 화면이다.

이때, 해당 서버 (이때 서버는 소셜 미디어 서버를 뜻한다) 의 전체 기능에 대한 권한에 접근할수 있을꺼라는 생각은 금물..!! 거의 왠만한 서비스에서 중요한 기능은 제공하지 않는 경우가 많다.

네이버의 경우도 위와 같은 기능에 한해서만 권한을 허용해준다. 여기서 "소셜 로그인" 기능을 위해 필요한 API는 "네이버 로그인" 이 되겠다.

 

네이버 로그인이라고 할지라도 그중에서 어떤 정보를 받을것인지 확인이 필요하다. 이때 꼭 필요한 정보만을 체크해야 한다!

나중에 검수를 진행하게 되는데 사용하지도 않는 정보를 요구했을 경우 이용이 제한될 수 있다. (담당자에게 전화온적도 있다... 검수하면서....)

 

그리고 나서 해당 서비스를 제공할 환경을 골라준다. 

이때 환경추가는 복수개로 등록이 가능하다. 서비스가 PC웹도있고, Mobile 웹도있고, 어플로도 존재하는 경우가 있을수 있으니 사용할 환경에 대한 정보를 모두 입력해 준다.

서비스 URL은 쉽게 서비스의 메인 url 을 입력하면 된다. (막혀있지 않은 공개된 url)

그리고 Callback URL은 네이버의 Authorization server를 통해 네이버 로그인창으로 로그인이 마무리 된후 보여줄 내 서비스의 페이지 url을 입력하면된다.

 

# 주의 ! Callback URL 갯수제한

이때 소셜로그인을 이동할수 있는 내 서비스의 페이지가 복수개라면 모두 입력해 주어야 한다.

여기서 저장되지 않은 callback URL을 나중에 간편로그인을 이용하면서 요청보내면 정상적으로 동작하지 않는다.

또한, callback url은 "최대 5개" 까지만 입력할 수 있으므로 유저 편의를 위한다고 여기저기서 소셜로그인이 가능하게 만들면 아주곤란해지므로 설계에 유의해야 한다.

 

본인도.. 5개 넘게 생각해뒀다가 제한에 걸려서 기능을 수정했던 경험이 있다 ㅎㅎ;;

 

[3] 어플리케이션 세팅하기

# 개요 : ClientID와 ClientSecret

어플리케이션을 등록하면 내 어플리케이션에 뜬다!

이때 ClientID와 ClientSecret 키가 발급된다. 딱봐도 한번에 노출 안된게 "노출하면 안되는거..!" 라는 느낌이 팍팍 드는 Client Secret은 "재발급" 이가능하므로, 혹시나 노출됐을경우 빠르게 재발급 하여 소중한 정보를 지키도록 하자!

개요 화면에서 보면 "네이버 로그인 검수요청" 이 보인다. 하지만 이를 진행하려면 아직 세팅할께 한참 남았으니 잠시 패스하고 다음 탭으로 넘어간다.

 

# API 설정

API 설정에서는 아까 어플리케이션 등록시에 설정했던 항목들을 볼 수 있다. 미쳐 설정을 놓친 부분이 있다면 이곳에서 다시 설정해 준다. 이때 "필수" 와 "추가"는 유저가 제공해야 할 정보에 대한 필수여부를 뜻하는데

 

"필수" 로 체크하면 유저가 이 서비스를 이용할시 반드시 제공해야 한다는 뜻이고,

"추가" 로 할 경우 서비스를 이용하는 유저가 선택적으로 해당 항목을 내 어플리케이션에 제공하겠다는 뜻이다.

 

만약 본인의 어플리케이션을 유저가 이용할때 필수로 받아야 하는 정보라면 "필수" 로 해놓고, 받아도 그만~ 안받아도 그만~ 이지만 받았을 경우 특별한 데이터 처리가 들어가는 경우엔 "추가" 로 받는다.

 

아까도 말했다싶이 사용하지도 않은 정보를 받으려 하면 검수과정에서 걸릴 수 있다!

 

+) 연락처 이메일!

여기서 "연락처 이메일 주소" 정보가 있다! 이는 해당 유저의 네이버 아이디가 아니다...!

네이버에 접속해 보면 "연락처이메일" 이라는 항목을 볼수 있는데 해당 정보를 제공받는 것으로 네이버 계정이지만 연락처이메일이 daum 이나 gmail 일수도 있다..

 

+) 로고 이미지

 

로고이미지를 등록해 준다. 이때 등록된 이미지는 유저가 우리 어플리케이션을 통해 네이버 로그인 진행시 정보제공 모달창에서 등장한다!

+) 연결 끊기

사용자가 내 어플리케이션에서 연동을 해제할 수 있지만, 네이버 계정관리의 Third party app 설정을 들어가 내 어플리케이션의 연결을 끊을 수도 있다! 이때 연결이 끊긴것에 대한 처리를 할 수 있도록 연결끊기 관련 Callback URL을 설정 할 수 있다.

 

# 네이버 로그인 검수 상태

해당 탭에서 검수에 필요한 자료를 첨부한다.

이 정보가 어디서 왜 필요한지에 대한 이미지 파일을 업로드 해야 한다.

참고로 사용하려는 기능이 "로그인" 이고, 로그인할때 제공받는 정보들이 있다면

 

로그인을 시작하는 단계부터 로그인 시 제공받은 정보가 활용되는 부분까지 전부 이미지 캡쳐해서 pdf 로 작성해야 한다.

 

+) 제공받은 정보가 노출되는 화면 캡쳐

 

+) 네이버로그인 API가 사용되는 흐름 캡쳐 : 총 12장 캡쳐

 

위 자료들이 준비 되었으면 다시 "개요" 화면으로 넘어가 "검수요청" 을 진행하면 된다

 

 

과정은 간단해 보이지만 정보 활용 내역을 검증하는데 굉장히 까다로운 심사로 수정에 수정을 거듭해야 한다.

아무래도 우리어플리케이션을 통해 네이버의 정보를 제공해주는거다 보니 까다로울 수 밖에 없기도 하다!

 

그만큼 개발하는 쪽도 개인정보에 대해 관리를 아주~ 잘~ 해주어야 겠다!

네이버 로그인 API를 활용한 기능이 활성화 되면 이런식으로 우리 어플리케이션을 통해 네이버 로그인을 사용하는 유저들의 이용량을 확인할 수 있다.

 

 

이상 소셜로그인중에서 그나마!! 담당자분도 한국분이시고, 문서 설명도 잘 되어있어

연동난이도가 가장~~~ 쉬웠던 네이버 소셜 로그인 이었다!

 

 

반응형
반응형

우리회사에서는 유저의 행동기록을 남기고 있다. 상품에 대한 처리, 주문에 대한 처리, 유저가 활동한 기록들 등..

이런 기록들이 서비스 오픈 이후로 별다른 정리조치 없이 꾸준히 쌓이고 있는데,

아무래도 서비스 운영기간이 늘어날수록 db용량이 늘어나는건 당연한 수순이지만, aws 비용 줄이기를 하고 있는 요즘

mongodb의 ec2 용량마저 줄이기 위해서 데이터 정리를 하게 되었다.

 

1. 현재 가용중인 mongodb의 ec2 용량 확인하기.

회사 mongodb는 aws의 ec2에 서버를 띄워 사용하고 있다. 그리고 EBS로 EC2의 용량을 지정해 두었다.

amazonEBS는 AWS 클라우드의 EC2 인스턴스에 사용할 영구 블록 스토리지 볼륨을 제공한다.
각 EBS 볼륨은 가용 영역 내에 자동으로 복제되어 구성요소 장애로부터 보호해주고 고가용성 및 내구성을 제공한다.

 

현재 설정된 mongodb EC2의 EBS 볼륨은 310GiB 이다.

왜 primary랑 secondary가 다르냐면... 바로 얼마전에 300GiB 에서 DB에 OOM이 발생하였기 때문이다 ㅎ

200GiB에서 올린지 1년도 채 되지 않았는데 그 사이에 유저들의 활동량이 폭발적으로 늘어나면서 금방 용량이 꽉 찬듯 하다.

이때 급해서 primary만 먼저 10GiB 정도 올리고 동시에 불필요한 히스토리 데이터를 삭제작업 하면서 최종적으로 가용중인 용량은 310GiB중 267GiB 이다.

 

+) ec2 mongodb 용량 확인하기 : df -h

[ec2-user@ip-**** ~]$ df -h
Filesystem      Size  Used Avail Use% Mounted on
/dev/nvme0n1p1  310G  267G   44G  87% /

 

미리 체크하면서 데이터 정리 했으면 안늘려도 됐겠군....ㅎ 그치만 데이터 정리에 대한 정책이 아예 없었었다!!!!!!!!!!!!!

 

이후 데이터 정리에 대한 여러번의.. 지시가 내려오면서... 다양한 기준으로 데이터 정리를 하기 시작했다.

- 장기 미접속 유저의 데이터 삭제...

- 탈퇴유저의 즉시 데이터 삭제..

그리고 이번에 작업한 "1년지난 히스토리 데이터 삭제" 이다.

 

2. 데이터 삭제하기

+) index 걸기

데이터 삭제를 위해서 기준값을 갖고 delete를 해주는데, 이때 잡은 기준값 key에 index가 몇개 collection엔 없었다.

일단 해~ 하면서 삭제프로세스를 시작했고 예상되는 소요시간 120시간 ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ

 

바로 중단 후 index 작업을 해주었다.

그리고 나서 걸린 시간 2시간! 새로운 collection를 구상할땐 index 꼭좀 걸어주세용... 뒤늦게 걸려니 영...


 

데이터 삭제 후 다시 가용용량을 확인해 봤는데 용량이 전혀 줄지 않았다! 띠용..

이는 "mongodb에서 삭제된 데이터에 대한 디스크 용량을 즉시 반환하지 않기 때문" 이다.

데이터를 삭제한 이유가 용량확보를 위한 것이므로 즉시 용량을 확보해주면 된다.

 

3. 용량 확보하기 : compact

 

+ collection lock 확인

mongodb에서 용량을 확보하는 명령어는 compact 이다.

해당 명령어 사용시 collection에 lock은 걸리지 않지만 가장 안전한 방법으로는

- 현재 사용중인 db와 를 복제..

- 데이터 마이그레이션 ..

이렇게 하는거라고 하는데 몇천만개의 데이터를 마이그레이션 하려면 거진 하루정도는 서비스를 내려야 하지 않나 싶다 ㅎ

아니면 내리지 않고 ... 어.. 음.. 마이그레이션 하는동안 생성된 데이터를 임시보관 해뒀다가 마이그레이션 한 뒤 다시 insert 해주는...? 어우.. 이게 맞나..? 무튼 복잡하다!


당장의 용량확보가 최우선순위였고, 삭제된 데이터는 운영하는데 아무런 지장이 없는 데이터이므로 운영환경에서 compact 해주기로 한다.

 

먼저 ec2의 mongodb 로 접속한다. 그리고 다시한번 용량 확인

[ec2-user@ip-*** ~]$ ssh 운영환경 ec2
       __|  __|_  )
       _|  (     /   Amazon Linux 2 AMI
      ___|\___|___|
[ec2-user@ip-*** ~]$ ssh mongo
Last login: ~~~

       __|  __|_  )
       _|  (     /   Amazon Linux 2 AMI
      ___|\___|___|
[ec2-user@ip-*** ~]$ df -h
Filesystem      Size  Used Avail Use% Mounted on
/dev/nvme0n1p1  310G  267G   44G  87% /

 

이후 mongodb shell 을 열어준다.

[ec2-user@ip-*** ~]$ mongo
MongoDB shell version v5.0.13
connecting to: mongodb://127.0.0.1:27017/? ~~~~~~
================
... 안내문 생략 ...
================
replica:PRIMARY>

db가 replica set 설정이 되어있어 primary 로 연결되었다.

 

+) compact 명령어 실행 권한 에러

compact 명령어는 db의 관리자가 수행할수있는 명령어이다. 관리자 권한이 아닌 상태에서 명령어 실행시 에러가 발생한다.

replica:PRIMARY> db.runCommand( {compact : '{target Collection}', force : true} )
{
	"ok" : 0,
	"errmsg" : "command compact requires authentication",
	"code" : 13,
	"codeName" : "Unauthorized",
	"$clusterTime" : {
		"clusterTime" : Timestamp(~~~, ~~),
		"signature" : {
			"hash" : BinData(0,"~~~"),
			"keyId" : NumberLong("~~")
		}
	},
	"operationTime" : Timestamp(~~~, ~~)
}

 

때문에 대상 db로 들어가 관리자로 접속후 진행해야 한다.

현재 관리자 user 생성된게 없어서 먼저 생성부터 해준다. shell 에서 생성해주려나 authentication error 가 발생하는데 이부분은 어떻게 생성하는지 방법을 몰라서 mongodb tool 에서 해당 db의 users 항목에서 add 해주었다.

db.createUser(
   {
     user: "dbAdmin", pwd: "password", roles: [{"role":"dbAdmin","db":"targetDB"}]
   }
)

 

이후 다시 mongo shell 에서 어드민 권한을 사용해 compact를 수행하준다.

replica:PRIMARY> use {작업할 DB명 : 이하 targetDB}
switched to db targetDB
replica:PRIMARY> db.auth('dbAdmin123123', 'password')
Error: Authentication failed.
0  # 접속 실패
replica:PRIMARY> db.auth('dbAdmin', 'password')
1  # 접속 성공

1 이 나오면 관리자 권한으로 연결됐단 뜻이다.

 

이후 compact 명령어를 실행해준다. 시간이 오래 걸릴 경우 shell 이 멈춘것처럼 보이는데 이때 다른 터미널을 열어서 mongo 용량을 확인해보면 실시간으로 용량이 줄어드는걸 볼 수 있다 ㅎㅎ

replica:PRIMARY> db.runCommand( {compact : '{target Collection}', force : true} )
{
	"bytesFreed" : ******,
	"ok" : 1,
	"$clusterTime" : {
		"clusterTime" : Timestamp(***, *),
		"signature" : {
			"hash" : BinData(0,"******"),
			"keyId" : NumberLong("******")
		}
	},
	"operationTime" : Timestamp(***, *)
}
# 작업 후 db 용량
Filesystem      Size  Used Avail Use% Mounted on
/dev/nvme0n1p1  310G  266G   45G  86% /

 1 기가 줄었군......................

 

이후로도 계속 작업을 진행해준다. 보다 상세한 비교를 위해 compact 수행 전 collection의 stats를 확인해보고 명령어 실행 이후와 함께 비교해 보았다.

확인 할 부분은 "storageSize" 이다. 데이터를 삭제하기 전과 용량이 동일하게 잡혀있다.

compact 명령어 실행 이후는 아래와 같다.

7.5GiB 로 용량이 줄어들었다!

전체 db 용량도 확인해보니 259GiB로 확인된다.

[ec2-user@ip-*** ~]$ df -h
Filesystem      Size  Used Avail Use% Mounted on
/dev/nvme0n1p1  310G  259G   52G  84% /

 

임무 완료!

반응형

+ Recent posts