제가 경험했던 OAuth를 정리하기 위해 포스팅합니다.

카카오, 네이버 로그인을 시도해보았고, 차례대로 포스팅하겠습니다.

 

먼저 카카오입니다.

 

우선 kakao developers 페이지에 들어가서 회원가입/로그인을 진행합니다.

그 다음 화면 상단의 "내 애플리케이션"을 클릭하여 들어옵니다.

여기서 "애플리케이션 추가하기"를 누릅니다.

 

정보를 입력하고 저장 버튼을 누릅니다.

본인이 만든 애플리케이션이 추가가 되었으니 들어갑니다.

들어오자마자 앱 키가 뜹니다.

당연히 중요한 키들이니 노출되지 않도록 주의해야 합니다.

여기서 사용할 키는 "REST API 키" 입니다.

키 등록은 SpringBoot에서 할 예정이니 왼쪽 메뉴의 "플랫폼"으로 넘어갑니다.

 

여기서 본인의 프로젝트에 맞는 플랫폼을 등록합니다.

저는 Web을 작성하였습니다.

 

그 다음은 카카오 로그인을 활성화 합니다.

상태가 OFF인데, 눌러서 ON으로 변경합니다.

그 밑에 있는 Redirect URI는 로그인이 성공했을 때 이동할 URI를 작성해주시면 됩니다.

구분은 엔터로해서 최대 10개를 등록할 수 있습니다.

local에서 테스트하는 uri와 배포를 하셨다면 배포uri를 넣어줍니다.

그 다음 동의항목입니다.

애플리케이션에서 사용할 동의항목을 설정합니다.

하지만 카카오계정(이메일)과 같은 중요한 정보는 "필수 동의"가 안되거나 "권한 없음" 설정이 되어있는 항목이 있습니다.

이런 경우에는 "비즈니스 설정 바로가기"에 들어가서 사업자 정보를 등록해야 더 많은 권한을 받을 수 있습니다.

회사에서 진행중인 프로젝트라면 가능하겠지만 학생 또는 교육생이거나 개인 프로젝트에서는 등록하기 어렵다고 볼 수 있습니다.

 

동의항목 설정이 끝났다면 보안으로 넘어갑니다.

Client Secret 코드를 발급 받아 보안을 강화할 수 있습니다.

되도록이면 발급 받는 것을 권장합니다.

토큰 발급을 받았더라면 활성화 상태를 "사용함"으로 변경합니다.

기본값은 "사용안함"입니다.

 

 

그러면 이제 SpringBoot로 넘어갑니다.

https://developers.kakao.com/docs/latest/ko/kakaologin/rest-api

 

Kakao Developers

카카오 API를 활용하여 다양한 어플리케이션을 개발해보세요. 카카오 로그인, 메시지 보내기, 친구 API, 인공지능 API 등을 제공합니다.

developers.kakao.com

우선 REST API 요청 관련은 문서를 참고해주시면 됩니다.

 

# gradle
implementation 'org.springframework.boot:spring-boot-starter-oauth2-client'

# Maven
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>

먼저 OAuth를 위한 의존성을 추가합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
spring:
  security:
    oauth2:
      client:
        provider:
          kakao:
            authorization-uri: https://kauth.kakao.com/oauth/authorize # 카카오 로그인 요청 uri
            token-uri: https://kauth.kakao.com/oauth/token # 토큰을 발급받기 위한 uri
            user-info-uri: https://kapi.kakao.com/v2/user/me # 유저 정보를 받아오기 위한 uri
        registration:
          kakao:
            client-id: # REST API Key
            client-secret: # Client Secret Key
            client-authentication-method: POST
            authorization-grant-type: authorization_code
            redirect-uri: # Redirect URI
cs

그 다음은 yml파일에 필수 정보를 넣어줍니다.

여기 넣은 정보들은 Java 코드에서 아래와 같이 사용할 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Value("${spring.security.oauth2.client.registration.kakao.client-id}")
private String kakaoClientId;
@Value("${spring.security.oauth2.client.registration.kakao.client-secret}")
private String kakaoClientSecret;
@Value("${spring.security.oauth2.client.registration.kakao.client-authentication-method}")
private String kakaoAuthenticationMethod;
@Value("${spring.security.oauth2.client.registration.kakao.authorization-grant-type}")
private String kakaoGrantType;
@Value("${spring.security.oauth2.client.registration.kakao.redirect-uri}")
private String kakaoRedirectUri;
@Value("${spring.security.oauth2.client.provider.kakao.authorization-uri}")
private String kakaoAuthorizationUri;
@Value("${spring.security.oauth2.client.provider.kakao.token-uri}")
private String kakaoTokenUri;
@Value("${spring.security.oauth2.client.provider.kakao.user-info-uri}")
private String kakaoUserInfoUri;
@Value("Bearer")
private String tokenType;
cs

 

 

글이 길어질 것 같아서 코드작성 부분은 다음 글에서 다루도록 하겠습니다.

https://www.acmicpc.net/problem/14466

 

14466번: 소가 길을 건너간 이유 6

첫 줄에 N, K, R이 주어진다. 다음 R줄에는 한 줄에 하나씩 길이 주어진다. 길은 상하좌우로 인접한 두 목초지를 잇고, r c r′ c′의 형태 (행, 열, 행, 열)로 주어진다. 각 수는 1 이상 N 이하이다.

www.acmicpc.net

소는 인접한 좌표로 이동할 수 있지만 길로 지정된 경로로는 길을 건너야 갈 수 있습니다.

문제에서는 길을 건너지 않으면 만날 수 없는 소의 쌍을 구하므로 "길로 지정된 경로로는 이동할 수 없다"로 접근하면 되겠습니다.

 

저는 소 한마리씩 이동을 하고, 이동하는 도중에 다른 소를 만난다면 카운팅을 진행하고, 전체 소의 갯수에서 제외해주는 방식으로 해결하였습니다.

 

저의 로직입니다.

  1. 2차원 pair를 이용하여 길의 정보를 넣습니다. 이 때, 양방향으로 넣어줍니다.
  2. 소의 좌표는 map에 인덱스를 표시해주고, 벡터에 넣어줍니다.
  3. 소 한마리씩 bfs로 이동하며, 주어진 길 이외에 인접한 좌표로 모두 이동시킵니다.
  4. 주어진 길인지 확인하는 방법은 해당 좌표에 연결된 길을 하나하나 찾는 방법을 선택하였습니다.
  5. 이동하는 과정에서 다른 소를 만난다면 ret을 1 증가합니다.
  6. 전체 소에서 ret과 1을 뺀 값을 리턴합니다. (여기서 1은 본인을 제외하는 것입니다.)
  7. 쌍을 구하는 문제이므로 ans/2를 출력합니다.

저는 예제가 계속 맞지않아서..

"아니 전체에서 만난 애들 뺀거랑 애초에 못만난 애들을 구한거랑 같은데 왜 다르게 나오지? 그럼 나머지 애들은 뭔데"

를 반복했었는데..

소의 수는 M인데.. 전체를 N으로 잡아서 발생한 삽질이었습니다. ^^..

저와같은 삽질을 하는 분은 없겠죠..?

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
#include <iostream>
#include <queue>
#include <vector>
#include <cstring>
#define MAX 101
using namespace std;
typedef pair<intint> pii;
 
vector<pii> list[MAX][MAX], v;
bool visit[MAX][MAX];
int map[MAX][MAX];
int direct[4][2= { {0,1},{1,0},{0,-1},{-1,0} };
int N, M, K;
 
int bfs(int sx, int sy) {
    queue<pii> q;
    visit[sx][sy] = true;
    q.push({ sx,sy });
    int num = map[sx][sy];
    int ret = 0;
    while (!q.empty()) {
        int x = q.front().first;
        int y = q.front().second;
        q.pop();
 
        if (map[x][y] && map[x][y] != num) {
            ret++;
        }
 
        for (int i = 0; i < 4; i++) {
            int nx = x + direct[i][0];
            int ny = y + direct[i][1];
 
            if (nx < 1 || ny < 1 || nx > N || ny > N) continue;
            if (visit[nx][ny]) continue;
 
            bool flag = true;
            for (auto a : list[x][y]) {
                if (a.first == nx && a.second == ny) {
                    flag = false;
                    break;
                }
            }
            if (!flag) continue;
 
            q.push({ nx,ny });
            visit[nx][ny] = true;
        }
    }
 
    return M - (ret + 1);
}
 
void func() {
    int ans = 0;
    int len = v.size();
    for (int i = 0; i < len; i++) {
        ans += bfs(v[i].first, v[i].second);
        memset(visit, 0sizeof(visit));
    }
 
    cout << ans / 2 << '\n';
}
 
void input() {
    int sx, sy, ex, ey;
    cin >> N >> M >> K;
    while (K--) {
        cin >> sx >> sy >> ex >> ey;
        list[sx][sy].push_back({ ex,ey });
        list[ex][ey].push_back({ sx,sy });
    }
 
    for (int i = 1; i <= M; i++) {
        cin >> sx >> sy;
        map[sx][sy] = i;
        v.push_back({ sx,sy });
    }
}
 
int main() {
    cin.tie(NULL); cout.tie(NULL);
    ios::sync_with_stdio(false);
 
    input();
    func();
 
    return 0;
}
cs

'algorithm > bfs' 카테고리의 다른 글

boj 2132 나무 위의 벌레  (0) 2024.06.17
boj 16985 Maaaaaaaaaze  (0) 2022.12.16
boj 18112 이진수 게임  (0) 2022.09.16
boj 16946 벽 부수고 이동하기 4  (0) 2022.05.22
boj 5547 일루미네이션  (0) 2022.05.15

sql에는 랜덤으로 row를 가져올 수 있는 query가 존재합니다.

"select * from word order by rand() limit 3;" 이런식으로 무작위로 3개 가져오는 쿼리도 실행이 가능합니다.

 

JPA는 메소드 이름으로 쿼리문을 실행해주는 편리한 인터페이스입니다.

"find + (Entity) + By + Column"의 형식의 메소드를 작성하면 이에 맞는 쿼리를 실행해줍니다.

친구와 미니 프로젝트를 하는 도중 랜덤 조회를 하기 위해 JPA 메소드를 작성하였습니다.

그렇게 차례대로 메소드를 써나가는데 OrderBy 뒤에 random과 관련된 단어가 보이지 않았습니다.

limit은 find 뒤에 Top, First, Last와 같은 키워드로 대체할 수 있습니다.

No property 'rand' found for type 'Word'

이렇게 어거지로 써봤지만 실행조차 되지않았습니다.

JPA 메소드에서 Order By는 컬럼명만 쓸 수 있으며, 랜덤 조회가 불가능하다는 결론이 나왔습니다.

https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#jpa.query-methods

 

Spring Data JPA - Reference Documentation

Example 109. Using @Transactional at query methods @Transactional(readOnly = true) interface UserRepository extends JpaRepository { List findByLastname(String lastname); @Modifying @Transactional @Query("delete from User u where u.active = false") void del

docs.spring.io

JPA 공식문서를 확인해도 random 관련 내용은 볼 수 없었습니다.

그리고 랜덤으로 N개를 조회하기 위해 "Top20"을 메소드에 추가하였지만 "20"이라는 고정된 값이기 때문에 변수로 바꾸려고 합니다.

 

우선 제가 생각해낸 방법은 @Query 어노테이션을 사용하는 방법입니다.

JPA 메소드 위에 @Query(value = "query string")를 붙이는 방법으로, 메소드명을 아무렇게 작성하더라고 위에 작성한 query대로 실행됩니다.

그래서 이렇게 쓰긴 했는데 에러가 발생합니다??

 

구글링으로 nativeQuery라는 것을 발견하였고,

nativeQuery = true면 SQL, nativeQuery = false면 JPQL 문법으로 구분한다고 합니다.

nativeQuery = false가 default라서 JPQL이라고 인식을 하고있지만 JPQL 문법에 맞지 않아 발생한 에러였습니다.

이제는 에러가 발생하지 않습니다.

실행 시에도 단어가 무작위로, cnt만큼 잘 나오는 것을 확인할 수 있습니다.

 

이 방법 외에도 다른 방법이 있는지 모르겠습니다.

@NamedQuery를 Entity에 붙여 사용해보았지만 @NamedQuery는 JPQL 문법만 사용이 가능한 것 같아 실패하였습니다. ㅠㅠ

 

알고리즘

1일 1알고리즘

 

영어 회화 스터디

이번주 야근을 많이 했어서 평일에는 스터디를 진행하지 못했다.

단어도 주말에 벼락치기로 외우느라 힘든 부분이 있었다.

하지만 장기 프로젝트이니.. 꾸준한게 답일듯 하다.

 

후기

벌써 10월. 2022년도 3달도 안남았고, 날씨도 추워지고 있다.

휴일에 집에만 있으니까 공부하는게 힘들고 그런데 카공을 하니 확실히 공부가 더 잘된다는 것을 느낀다.

앞으로 카페 자주 다녀야겠다.

'잡담' 카테고리의 다른 글

10월 3주차 결산  (0) 2022.10.24
10월 2주차 결산  (0) 2022.10.16
9월 5주차 결산  (0) 2022.10.02
9월 4주차 결산  (0) 2022.09.26
9월 3주차 결산  (0) 2022.09.18

https://www.acmicpc.net/problem/18113

 

18113번: 그르다 김가놈

첫 번째 줄에 손질해야 하는 김밥의 개수 N, 꼬다리의 길이 K, 김밥조각의 최소 개수 M이 주어진다. (1 ≤ N ≤ 106, 1 ≤ K, M ≤ 109, N, K, M은 정수) 두 번째 줄부터 김밥의 길이 L이 N개 주어진다.

www.acmicpc.net

김밥의 꼬다리를 K * 2 만큼 잘라낸 김밥을 손질된 김밥이라고 하며, K * 2보다 작은 김밥은 K만큼만 잘라낸다고 합니다.

그 손질된 김밥을 길이가 P인 조각으로 잘라내 최소 M개의 조각을 만드려고 합니다.

-> 최소 M개의 조각을 만들기 위한 최대 P를 구하는 문제로 파라매트릭 서치를 이용합니다.

 

파라매트릭 서치는 이분탐색과 큰 차이는 없으며,

이분탐색이 정확히 M인 값을 구하는 반면, 파라매트릭 서치는 M에 가장 가까운 최적의 값을 구하는 것입니다.

 

 

이 문제는 두 가지 방법을 선택하여 해결할 수 있습니다.

먼저, 모든 김밥에 대해 최적의 P를 구하는 방법입니다.

이 방법은 입력을 그대로 다 받아놓고, 파라매트릭 서치 과정에서 K * 2 or K를 빼는 식이 포함되는 방법입니다.

 

아니면, 애초에 K보다 작거나 K * 2와 같은 길이의 김밥을 먼저 제외하여 최적의 P를 구하는 방법입니다.

이 방법은 입력을 받을 때 미리 K * 2 or K를 빼는 식이 포함되는 방법입니다.

이 과정에서 꼬다리를 제거했을 때 길이가 0 이하가 되는 김밥을 제외합니다.

두 방법 모두 AC를 받는데는 문제가 없으나 두번째 방법이 더 적은 갯수로 구할 수 있으므로 시간상 이득을 볼 수 있습니다.

 

파라매트릭 과정에서 필요없는 김밥을 제외
입력 과정에서 필요없는 김밥을 제외

두 방법으로 제출했을 때 확실히 미리 제외한 방법이 시간상 효율적이었습니다.

 

파라매트릭 서치 과정에서는

l, r은 김밥조각의 길이인 P의 범위이며, 김밥을 m으로 나눈 몫을 카운팅한 값이 M 이상이 되는 최적의 해를 구해주시면 되겠습니다.

 

 

[필요없는 김밥을 제외하지 않은 코드]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
 
vector<int> list;
int N, K, M, l, r;
 
void func() {
    int ans = -1;
    l = 1;
    while (l <= r) {
        int m = (l + r) >> 1;
 
        int cnt = 0;
        for (int i = 0; i < N; i++) {
            if (list[i] <= K) continue;
 
            if (list[i] >= K * 2) cnt += ((list[i] - K * 2/ m);
            else cnt += ((list[i] - K) / m);
        }
 
        if (cnt >= M) {
            ans = m;
            l = m + 1;
        }
        else {
            r = m - 1;
        }
    }
 
    cout << ans << '\n';
}
 
void input() {
    int x;
    cin >> N >> K >> M;
    for (int i = 0; i < N; i++) {
        cin >> x;
 
        list.push_back(x);
        r = max(r, x);
    }
}
 
int main() {
    cin.tie(NULL); cout.tie(NULL);
    ios::sync_with_stdio(false);
 
    input();
    func();
 
    return 0;
}
cs

 

 

[처음부터 필요없는 김밥을 제외한 코드]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
 
vector<int> list;
int N, K, M, l, r;
 
void func() {
    int ans = -1;
    l = 1;
    while (l <= r) {
        int m = (l + r) >> 1;
 
        int cnt = 0;
        for (int i = 0; i < N; i++) {
            cnt += (list[i] / m);
        }
 
        if (cnt >= M) {
            ans = m;
            l = m + 1;
        }
        else {
            r = m - 1;
        }
    }
 
    cout << ans << '\n';
}
 
void input() {
    int x;
    cin >> N >> K >> M;
    for (int i = 0; i < N; i++) {
        cin >> x;
        if (x <= K || x == K * 2continue;
 
        int sub = x;
        if (x < K * 2) sub -= K;
        else sub -= (K * 2);
 
        list.push_back(sub);
        r = max(r, sub);
    }
 
    N = list.size();
}
 
int main() {
    cin.tie(NULL); cout.tie(NULL);
    ios::sync_with_stdio(false);
 
    input();
    func();
 
    return 0;
}
cs

'algorithm > binarysearch' 카테고리의 다른 글

boj 27977 킥보드로 등교하기  (4) 2023.04.22
boj 2295 세 수의 합  (0) 2022.06.28
boj 2110 공유기 설치  (0) 2021.04.13
boj 7453 합이 0인 네 정수  (0) 2021.01.22
boj 2143 두 배열의 합  (0) 2021.01.22

https://www.acmicpc.net/problem/2281

 

2281번: 데스노트

첫째 줄에 n(1 ≤ n ≤ 1,000), m(1 ≤ m ≤ 1,000)이 주어진다. m은 노트의 가로 칸의 개수(폭, 너비)이다. 다음 n개의 줄에는 각 사람의 이름의 길이가 노트에 적어야 할 순서대로 주어진다. 각 길이는 m

www.acmicpc.net

노트에 이름을 순서대로 적기 위한 조건은 아래와 같습니다.

  1. 위에서 아래로 적습니다.
  2. 같은 줄에서는 왼쪽에서 오른쪽으로 적습니다.
  3. 이름 사이에는 한 칸의 빈 칸이 있습니다.
  4. 한 사람의 이름은 한 줄에만 적어야 하며, 해당 줄에 사람의 이름을 다 적지 못하면 다음 줄에 적어야 합니다.
  5. 한 줄을 넘어가는 길이의 이름은 주어지지 않습니다.

위 조건을 지키면서 이름을 적으며, 마지막 줄을 제외한 모든 줄에서 끝에 남은 칸의 갯수의 제곱을 더한 값의 최소를 구하는 문제입니다.

 

dp[idx][len]: idx번째 이름을 적을 차례이고, idx - 1번째까지 len의 공간을 사용했을 때의 최솟값

여기서 선택할 수 있는 방법은

  1. 현재 줄을 빈 칸으로 놔두고 다음 줄에 이름을 적는다.
  2. 현재 줄에 남은 칸이 이름의 길이만큼 남았다면 현재 줄에 이름을 적는다.

이렇게 두 가지 있습니다.

1번은 모든 경우에 할 수 있으며, 2번은 남은 공간을 파악하여 구하도록 합니다.

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#include <iostream>
#include <algorithm>
#include <cstring>
#define MAX 1001
using namespace std;
 
int dp[MAX][MAX], list[MAX];
int N, M;
 
int solve(int idx, int len) {
    if (idx == N) return 0;
 
    int& ret = dp[idx][len];
    if (ret != -1return ret;
    
    ret = (M - len + 1* (M - len + 1+ solve(idx + 1, list[idx] + 1);
    if (len + list[idx] <= M) {
        ret = min(ret, solve(idx + 1, len + list[idx] + 1));
    }
 
    return ret;
}
 
void func() {
    memset(dp, -1sizeof(dp));
    cout << solve(00<< '\n';
}
 
void input() {
    cin >> N >> M;
    for (int i = 0; i < N; i++) {
        cin >> list[i];
    }
}
 
int main() {
    cin.tie(NULL); cout.tie(NULL);
    ios::sync_with_stdio(false);
 
    input();
    func();
 
    return 0;
}
cs

'algorithm > dp' 카테고리의 다른 글

boj 14450 Hoof, Paper, Scissors (Gold)  (0) 2022.12.30
boj 14453 Hoof, Paper, Scissors (Silver)  (0) 2022.12.30
boj 2015 수들의 합 4  (0) 2022.08.12
boj 21923 곡예 비행  (0) 2022.06.10
boj 12978 스크루지 민호 2  (0) 2022.06.08

https://whyeskang.com/258

 

Postman을 이용한 File, Dto 동시 Post요청

보통 Controller에서 Dto를 받을 때는 @RequestBody를 주로 사용합니다. 그리고 File을 받을 때는 MultipartFile 객체를 사용하며, @RequestParam을 사용합니다. 하지만 File과 Dto를 같이 받기 위해서는 @RequestPart라

whyeskang.com

작년에 이어 File, Dto 동시 요청 2탄입니다.

 

이전 글에서는 단일 File과 Dto를 동시에 요청받았습니다.

이번 글에서는 File과 Dto의 List를 Postman을 이용하여 요청하는 것을 다루려고 합니다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Entity
@Data
@NoArgsConstructor
public class User {
    @Id
    String userId;
    String password;
    String profileImageUrl;
 
    public User(UserSignUpReq userSignUpReq) {
        this.userId = userSignUpReq.getUserId();
        this.password = userSignUpReq.getPassword();
    }
}
cs

User Entity입니다.

테스트 용도이니 간단하게만 작성해줍니다.

1
2
3
4
5
@Data
public class UserSignUpReq {
    String userId;
    String password;
}
cs

RequestDto 또한 간단하게 해줍니다.

 

 

1
2
3
4
5
6
7
8
9
10
@PostMapping("/signup")
public ResponseEntity<List<UserGetRes>> signUpUser(
       @RequestPart(value = "multipartFileList") List<MultipartFile> multipartFileList,
       @RequestPart(value = "userSignUpReqList") List<UserSignUpReq> userSignUpReqList) {
    return new ResponseEntity<>(
            userService.loginUser(
                    multipartFileList,
                    userSignUpReqList.stream().map(userSignUpReq -> new User(userSignUpReq)).collect(Collectors.toList())),
            HttpStatus.OK);
}
cs

그 다음 요청을 받을 Conrtoller입니다.

어노테이션은 @RequestPart로 이전과 같으며, 요청을 정상적으로 받았는지 확인하기 위해 List<ResDto>를 리턴합니다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
@Override
public List<UserGetRes> loginUser(List<MultipartFile> multipartFileList, List<User> userList) {
    try {
        String separ = File.separator;
        String today = new SimpleDateFormat("yyMMdd").format(new Date());
 
        File file = new File("");
        String rootPath = file.getAbsolutePath().split("src")[0];
        String savePath = rootPath + separ + "profileImage" + separ + today;
        if (!new File(savePath).exists()) {
            try {
                new File(savePath).mkdirs();
            } catch (Exception e) {
                e.printStackTrace();
                return null;
            }
        }
 
        int len = multipartFileList.size();
        for (int i = 0; i < len; i++) {
            // multipartFileList와 userList의 길이가 무조건 같고, 순서가 일치하다고 가정한다.
            MultipartFile multipartFile = multipartFileList.get(i);
 
            String originFileName = multipartFile.getOriginalFilename();
            String saveFileName = UUID.randomUUID().toString() + originFileName.substring(originFileName.lastIndexOf("."));
 
            String filePath = savePath + separ + saveFileName;
            multipartFile.transferTo(new File(filePath));
 
            userList.get(i).setProfileImageUrl(filePath);
        }
 
        return userRepository.saveAll(userList).stream().map(user -> UserGetRes.of(user)).collect(Collectors.toList());
    }
    catch (Exception e) {
        e.printStackTrace();
        return null;
    }
}
cs

받은 요청을 처리할 Service입니다.

File과 User List의 길이와 순서가 같다고 가정하고 구현한 코드입니다.

중요한건 "File과 User의 List를 같이 받는 것" 입니다.

파일을 지정한 경로에 저장한 후 DB에 추가하는 로직이며, 이전 글에 List만 추가되었습니다.

 

우선 단일 File, Dto 요청 시에는 Body -> form-data에서 File 업로드 및 Dto는 json 방식으로 작성하면 된다고 하였습니다.

 

 

아 맞다 contentType 추가해야하는데

 

Dto 쪽에는 contentType을 application/json으로 맞춰줘야 합니다.

 

 

List를 추가한다고 해서 크게 달라질건 없었습니다.

File은 같은 value로 원하는 수만큼 추가하면 되고,

Dto는 똑같이 json 방식으로 작성해주고, List가 되었으니 대괄호 "[]" 만 추가하면 되겠습니다. (중괄호 "{}" 아닙니다!)

 

이제 Send를 누르면 데이터가 잘 들어가는 것을 확인할 수 있습니다.

 

확인해야할 부분은

Postman에서 입력할 KEY와 @RequestPart의 value로 지정한 값이 일치해야 한다는 것

같은 KEY로 파일을 등록하면 List가 된다는 것

Dto는 form-data에서 json 방식으로 등록해야 하며, List는 대괄호를 추가해야 한다는 것

Dto의 contentType을 application/json으로 맞춰야 한다는 것

정도가 되겠습니다.

 

알고리즘

1일 1알고리즘

 

영어 회화 스터디

꾸준히 단어암기 및 회화 진행중이다.

퇴근하고 나면.. 쉬고싶어져서 쉽지않다. 내 의지 문제인듯

 

후기

확실히 흥미를 가지는 알고리즘은 바쁘든 말든 꾸준히 하는 반면에 영어는 확실한 의지를 가져야 할것 같다.

스터디원도 그런 어려움을 겪어서인지 이번주는 많은 공부를 하지 못했다.

다음주에는 더 열심히 하는 한 주가 되었으면 좋겠다.

'잡담' 카테고리의 다른 글

10월 2주차 결산  (0) 2022.10.16
10월 1주차 결산  (0) 2022.10.10
9월 4주차 결산  (0) 2022.09.26
9월 3주차 결산  (0) 2022.09.18
9월 2주차 결산  (0) 2022.09.11

알고리즘

1일 1알고리즘

 

영어 회화 스터디

이번주에 배정된 단어를 외우고 시험을 보는 식으로 진행 중이고, 회화는 시간 될 때마다 온라인으로 진행 중이다.

친구가 개발에도 관심이 있어 갑자기 단어 시험 웹 페이지를 만들자는 얘기가 나왔고, 프로젝트 진행 중이다.

 

후기

단어가 너무 많다.. 언제 다외우지?

회사에서도 규모가 적지만 개발에 들어갔고, 배울 사람이 있어 도움이 많이 되는것 같다.

내가 못해봤던 것들은 공부부터 해야해서 시간이 좀 걸리지만 뒤쳐지지 않도록 열심히 해야겠다.

'잡담' 카테고리의 다른 글

10월 1주차 결산  (0) 2022.10.10
9월 5주차 결산  (0) 2022.10.02
9월 3주차 결산  (0) 2022.09.18
9월 2주차 결산  (0) 2022.09.11
9월 1주차 결산  (0) 2022.09.05

알고리즘

1일 1알고리즘

 

영어 회화 스터디

회화는 하고있지만 단어를 모르니 막히는 부분이 너무 많다.

단어 책도 구매했으니 꾸준히 해야겠다.

 

후기

오랜만에 자바스크립트를 봤더니 원래 모르는게 많았지만 까먹은 것도 많았다.

진짜 공부란 끝이 없는 것 같다..

다음주도 화이팅!

'잡담' 카테고리의 다른 글

9월 5주차 결산  (0) 2022.10.02
9월 4주차 결산  (0) 2022.09.26
9월 2주차 결산  (0) 2022.09.11
9월 1주차 결산  (0) 2022.09.05
8월 4주차 결산  (0) 2022.08.29

https://www.acmicpc.net/problem/18112

 

18112번: 이진수 게임

첫 번째 줄에 길이 L의 ‘시작 이진수’가 주어진다. 두 번째 줄에 길이 K의 ‘목표 이진수’가 주어진다. (1 ≤ L, K ≤ 10)

www.acmicpc.net

  1. 맨 앞 숫자를 제외한 한 자리 숫자를 보수로 바꾸기
  2. 현재 수에 1 더하기
  3. 현재 수에 1 빼기 (현재 수가 자연수일 때만 해당)

위 과정을 반복하여 시작 이진수 s가 목표 이진수 e가 되는 최소 동작 횟수를 출력하는 문제로 오랜만에 bfs입니다.

입력은 10자리 이진수로 주어지며, 저는 이 수를 10진수로 변환하여 비트마스킹을 이용하였습니다.

 

이 풀이의 로직은

먼저, 이진수를 10진수로 변환하고, s를 시작으로 bfs 탐색합니다.

 

그 다음은 x가 0인 경우를 제외하고 한 비트씩 변경된 값을 queue에 넣습니다.

이 과정에서 bit가 x / 2보다 커진다면 마지막 비트이므로 break를 걸어줍니다.

 

e가 10자리 이진수이므로 1024보다 작은 수입니다.

따라서 x + 1이 1024보다 작은 경우에만 queue에 넣어주면 됩니다.

 

그리고 e는 음수가 아니므로 x가 자연수일 경우에만 x - 1을 queue에 넣어줍니다.

 

위의 과정을 반복하여 e에 도착하면 t를 리턴하여 출력합니다.

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
#include <iostream>
#include <queue>
#include <string>
#define MAX 10
using namespace std;
 
string str1, str2;
bool visit[1 << MAX];
int s, e;
 
int bfs() {
    queue<int> q;
    q.push(s);
    visit[s] = 1;
    for (int t = 0!q.empty(); t++) {
        int qsize = q.size();
        while (qsize--) {
            int x = q.front();
            q.pop();
 
            if (x == e) {
                return t;
            }
 
            int bit = 1;
            while (1) {
                if (!x) break;
                if (x & bit) {
                    if (bit > x / 2break;
                }
 
                int next = x ^ bit;
                if (!visit[next]) {
                    q.push(next);
                    visit[next] = true;
                }
                bit <<= 1;
            }
 
            if (x + 1 < 1024 && !visit[x + 1]) {
                q.push(x + 1);
                visit[x + 1= true;
            }
            if (x && !visit[x - 1]) {
                q.push(x - 1);
                visit[x - 1= true;
            }
        }
    }
 
    return 0;
}
 
void func() {
    cout << bfs() << '\n';
}
 
void init() {
    int len1 = str1.size();
    int mul = 1;
    for (int i = len1 - 1; i >= 0; i--) {
        if (str1[i] == '1') s += mul;
        mul <<= 1;
    }
 
    int len2 = str2.size();
    mul = 1;
    for (int i = len2 - 1; i >= 0; i--) {
        if (str2[i] == '1') e += mul;
        mul <<= 1;
    }
}
 
void input() {
    cin >> str1 >> str2;
    init();
}
 
int main() {
    cin.tie(NULL); cout.tie(NULL);
    ios::sync_with_stdio(false);
 
    input();
    func();
 
    return 0;
}
cs

'algorithm > bfs' 카테고리의 다른 글

boj 16985 Maaaaaaaaaze  (0) 2022.12.16
boj 14466 소가 길을 건너간 이유 6  (0) 2022.10.13
boj 16946 벽 부수고 이동하기 4  (0) 2022.05.22
boj 5547 일루미네이션  (0) 2022.05.15
boj 16932 모양 만들기  (0) 2022.05.14

알고리즘

1일 1알고리즘

 

영어 회화 스터디

회사 생활 하면서 가장 느꼈던 것이 영어다.

영어를 안한지 너무 오래돼서 무슨 말인지 모르겠더라 ㅎㅎ

그래서 같이 영어공부할 친구를 구해서 스터디를 시작했다.

물론 어떻게 공부해야 할 지 몰라서 추천받은 방식으로 일단 진행 해보려고 한다.

 

후기

사실 영어를 좀더 일찍 했어야 했는데 내 의지 문제였던걸로 ㅎㅎ..

지금이라도 시작했고, 단어나 문법을 몰라도 회화를 시도해보려고 한다.

얼마나 걸릴진 모르겠다.

이번주 추석이라 본가에 다녀왔다.

취업을 하고 처음 방문이라 좀더 새로웠던 것 같다.

월급도 받았다보니 부모님께 용돈을 처음으로 드렸고, 좋아하시는 모습을 보고 뭔가 뿌듯했다.

내일은 쉬려고 빨리 올라왔지만 자주 내려가야겠다.

'잡담' 카테고리의 다른 글

9월 4주차 결산  (0) 2022.09.26
9월 3주차 결산  (0) 2022.09.18
9월 1주차 결산  (0) 2022.09.05
8월 4주차 결산  (0) 2022.08.29
8월 3주차 결산  (0) 2022.08.22

+ Recent posts