2. char to int, string to int 매번 헷갈리나 char는 간단하게 - '0'을, string의 경우 stoi를 쓰면 된다.
중요하게 생각했던 점
1. 내 위치를 저장하는 구조 pair를 사용하여 한 묶음으로 가지고 다녔다. 배열을 사용해도 되나 인지적으로 0번이 세로고, 1이 가로임을 생각하는 것이 불편했다. first와 second로 생각하면 현재 다루고 있는 배열과 다른 생각 묶음으로 가져갈 수있어서 구분이 쉽다고 생각했다.
2. 예외 조건 생각하기 동서남북으로 point를 변경하여 조건을 매번 확인해줘야 하는데 동 서 남 북으로 반복문을 작성하고 확인하면 코드가 늘어나서 이동은 switch 문을 사용해서 코드의 양을 줄이도록 해봤다.
3. 레이블을 이용한 goto문 악마의 goto문이지만 이중 for문을 나갈 때 유용해서 애용해보려고 써봤습니다. 좋네요. 기존에는 bool check사용해서 한번 더 if문을 확인했는데 이중 for문에 한해서는 사용하는 것이 코드도 깔끔하고 좋네요.
#include <string>
#include <vector>
#include <iostream>
using namespace std;
// 로봇 강아지에 미리 입력된 명령에 따라 진행하며, 명령은 다음과 같은 형식으로 주어집니다.
// ["방향 거리", "방향 거리" … ]
// "E 5"는 로봇 강아지가 현재 위치에서 동쪽으로 5칸 이동했다는 의미입니다.
// 명령 수행하기 전에 확인
// 👾 주어진 방향으로 이동할 때 공원을 벗어나는지 확인합니다.
// 👾 주어진 방향으로 이동 중 장애물을 만나는지 확인합니다.
// 위 두 가지중 어느 하나라도 해당된다면, 로봇 강아지는 해당 명령을 무시하고 다음 명령을 수행합니다.
vector<int> solution(vector<string> park, vector<string> routes) {
vector<int> answer;
// 배열로 관리하는 방법도 있을 것 같음
pair<int, int> point = { 0, 0 };
int h = park.size();
int w = park[0].length();
// 배열의 크기가 3칸임으로 3칸이 나온다. 2칸이 아니라, index는 0부터 시작임으로 =으로 해야함.
for(int i=0; i<h; i++) {
for(int j=0; j<w; j++) {
if(park[i][j] == 'S') {
point = { i, j };
goto start;
}
}
}
start:
for(string route: routes) {
pair<int, int> temp = point;
const char op = route[0];
// char를 int로 변경하는 방법으로 - '0'이 있고, (int) <- 앤 잘 안 됨
const int n = route[2] - '0';
// string to i => stoi
// const int n = stoi(route[2]);
for(int i=0; i<n; i++){
switch(op) {
case 'N': point.first -= 1; break;
case 'S': point.first += 1; break;
case 'W': point.second -= 1; break;
case 'E': point.second += 1; break;
}
// 범위가 w-1, h-1이라 =을 해줬는데 이게 의식적으로 잘 안 그려짐
if(point.first < 0 || point.first >= h ||
point.second < 0 || point.second >= w) {
point = temp;
break;
} else if(park[point.first][point.second] == 'X') {
point = temp;
break;
}
}
}
answer.push_back(point.first);
answer.push_back(point.second);
return answer;
}
여기서 th:inline을 사용하면 js 변수 내에 [[${ }]]로 가능하다. 이게 왜 좋냐면 th:inline을 사용하지 않으면 [[${}]] 양 옆에 /* */를 사용해야 한다. 그리고 외부 Js에서는 이렇게 사용할 수가 없다. 그런 이유는 html을 렌더링? 서버에서 템플릿 작업을 해주면서 템플릿 언어에 해당하는 문법을 보고 작업을 해준다.
그렇기 때문에 html에 외부 js 파일이 내장되어 있지 않으니 정상적으로 변수에 값이 들어가지 않는 것이다.
이렇게 라이브러리를 단순히 추가함으로 왼쪽(추가 전)에 되던 통신이 오른쪽처럼 401로 막히게 됩니다. 정확하지는 않지만, 실제 코드 실행 시 막히는 현상을 파악했습니다.
TIP ^V^
401은 사용자가 로그인이 되어 있지 않은 상태 => 인증 실패 (개발자는 앱 설계 시 자격 증명이 누락되거나 잘못되었을 때 ) 403은 사용자가 권한이 없는 상태 => 권한 부여 실패, 호출자를 식별하였지만, 이용 권리 없음을 나타냄
2. 스프링 시큐리티 인증 절차 설명
조금 복잡하게 되어있지만 하이라이트만 확인해보면 좋겠습니다. Oauth2.0을 사용하면 위에 정의된 아키텍쳐에 들어가는 구성 요소의 이름이 조금 바뀝니다.
1. 인증 필터 (Authentication Filter): 인증 요청을 인증 관리자에게 위임하고 응답을 바탕으로 보안 컨텍스트를 구성함 2. 인증 관리자 (Authentication Manager): 인증 공급자를 이용하여 인증을 처리한다. 3. 인증 공급자 (Authentication Provider): - 사용자 관리 책임을 구현하는 사용자 세부 정보 서비스를 인증 논리에 이용한다. - 암호 관리를 구현하는 암호 인코더를 인증 논리에 이용한다. 4. 유저 세부 정보 서비스 (User Details Service) 5. 보안 컨텍스트(Security Context): 인증 프로세스 후 인증 데이터를 유지한다.
3. 자동으로 구성되는 빈
1. UserDetailsService 2. PasswordEncoder 인증 공급자 (Authentication Provider)는 위 두 개의 빈을 이용하여 1)사용차를 찾고, 2) 암호를 확인합니다.
사용자 정보 관리하는 UserDetailsService
스프링 시큐리티에서 기본적으로 제공하는 기본 구현을 이용했지만, 우리는 입맛에 맞게 바꿔서 사용한다.
1차 정리
1. 기본적으로 UserDetailsService와 PasswordEncoder가 Basic 인증 흐름에 꼭 필요하다는 사실을 인지해야 한다. 2. Basic 인증에서는 클라이언트가 사용자 이름과 암호를 HTTP Authorization 헤더를 통해 보내기만 하면 된다. => 헤더 값에 접두사 Basic을 붙이고 그 뒤에 :으로 구분된 사용자 이름과 암호가 포함된 문자열을 Base64 인코딩하여 붙인다.
4. 문제🙄: 기존 websecurityconfigureradapter deprecated
기존에 사용하던 WebSecurityConfigureradapter가 Deprecated 되었다. 거의 대부분 인터넷이나 서적을 찾아보면 이를 사용하는 코드가 적혀있지만, 현재 기준으로 Deprecated가 되어 다른 코드를 사용해야 한다. 자세한 내용은 아래 하단 링크를 통해 확인하면 된다.
이 클래스를 생성하는 이유는 Security Context에 저장되는 인증 객체를 만들기 위함입니다. 이 클래스는 추후 컨트롤러에서 파라미터로 받아오는 객체의 클래스입니다.
먼저 코드를 살펴보면 UserDetails를 구현합니다. 코드를 확인해봅시다.
import com.example.demo.User;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collection;
import java.util.stream.Collectors;
public class CustomUserDetails implements UserDetails {
private final User user;
public CustomUserDetails(User user) {
this.user = user;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return user.getAuthorities().stream().map(a -> new SimpleGrantedAuthority(a.getName())).collect(Collectors.toList());
}
@Override
public String getPassword() {
return user.getPassword();
}
@Override
public String getUsername() {
return user.getUsername();
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
public final User getUser() {
return user;
}
}
실제 코드를 확인해보면 위와 같은 주석이 있습니다.
Authentication 객체에 캡슐화가 된다는 부분이 중요합니다. 왜냐하면 나중에 Security Context에 넣을 때 Authentication 객체만 들어가기 때문입니다. 그래서 우리는 User 객체를 가지고 있지만 Authentication으로 캡슐화가 될 수 있는 UserDetails를 구현합니다.
일단 문제를 확인해보면 1부터 3까지 총 3 가지 수로 주어진 N의 더하기 경우의 수를 구하는 문제임을 알 수 있다.
조건이 있는데 연속된 수를 사용하면 안 된다란 조건이 있다. => 곧 힌트란 의미.
먼저 이를 어떻게 구하는 지 고민을 해보면 완전탐색을 생각할 수 있다.
각 경우의 수가 총 2개이다.
1 깊이에서 3개, 2 깊이에서 2개씩 총 6개, 3 깊이에서도 2개씩 총 6*2 12개이다. 4 깊이에서는 24개
결국 깊이에서의 경우의 수가 3, 6, 12, 24 순으로 늘어난다. 무슨 수열인지는 모르겠지만 자신의 앞의 수 * 2씩 늘어난다. 딱 봐도 엄청 느리다.
그래도 수식을 구해야하니까 수열에 공통적으로 3이 곱해져 있으니, 앞으로 빼보니 3*2^n 이다. 문제에서는 n이 100,000보다 작거나 같다고 하니 3*2^n은 계산기도 오버플로우가 난다. 2^100 만 해도 1,267,650,600,228,229,401,496,703,205,376가 나오니 시도조차 하면 안 되는 방법이다.
어차피 완전탐색을 그리다보면 같은 경우의 수를 탐색하며, 똑같은 상태를 매번 계산한다는 점이 느껴진다. 그럼으로 DP로 풀 수 있다고 느껴진다고 해야한다.( 사실 문제의 유형을 보고 풀어서 ... )
이 문제는 조건이 있었다. 만약 없다면 어떻게 풀까?
5를 구한다면 2, 3, 4의 수를 더하면 된다.
근데 이 문제는 연속된 경우를 피해야 한다. 어떻게 피할 수 있을까? 일단 일차원 배열의 경우, 안에 중첩된 경우를 분간하지 못한다.
Oauth2.0에 대해 작년에 아무런 지식없이 인터넷을 방황하며 코드를 작성했었다. 당시 Nestjs로 작성했었는데 이제는 다른 프레임워크로 작성해야했다. 이번에는 스프링 부트로 oauth를 이용하려고 하고 라이브러리를 찾아본 결과 시큐리티 5에 있는 oauth2.0-client를 이용했다.
어려운 부분은 크게 2가지였다.
1. 트위치 Oauth에 적용하기 2. Oauth2.0에 대한 지식
해결했던 방법
1. 트위치 Oauth2.o에 적용을 해결했던 방법
카카오와 네이버도 스프링 Oauth2.0 라이브러리에서 제공하는 기본 제공자가 아니다. 이 두개의 적용 방법과 트위치 개발자 페이지와 검색을 통해서 해결했다.
권한 코드는 백엔드에서 처리하는 방식에 적합하고 위 설정 파일에서spring.security.oauth2.client.registration.twitch.authorization-grant-type = authorization_code 라고 적은 부분도 어떤 방식을 이용하겠다고 설정한 것이다.