728x90

헷갈렸던 부분

1. 배열의 size 고려

  • 주어진 방향으로 이동할 때 공원을 벗어나는지 확인합니다.
    해당 조건을 고려하기 위해서는 배열의 크기를 제대로 인지하고 있어야 하는데 매번 문제 풀 때마다 이 부분이 악취가 나는 부분이었습니다.

size()는 배열이 3칸이면 2를 반환하는 것이 아닌 3을 반환한다.
즉 index는 0부터 시작함으로 우리가 조건을 확인할 때 -1을 하거나 =으로 확인해줘야 한다.

Q. 인지의 오류를 줄일 수 있는 방법은 없을까?

애초에 w와 h에 -1을 하고 =을 사용하면 조금 더 인지적 오류 없이 사용하기 좋을 것 같다는 생각이 드네요.

int h = park.size() - 1;
int w = park[0].size() - 1;

for(int i=0; i<= h; i++) {
        for(int j=0; j<= w; j++) {
            if(park[i][j] == 'S') {
                point = { i, j };
                goto start;
            }
        }
    }
for문은 배열의 내부를 돌기 때문에 =을 사용하고

if(point.first < 0 || point.first > h ||
               point.second < 0 || point.second > w) {
                point = temp;
                break;
 } 

범위를 나갔는지 확인할 때에는 >으로 확인하면 되니 인지적 오류가 줄어들 것 같습니다.

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;
}

https://school.programmers.co.kr/learn/courses/30/lessons/172928

 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr

다른 사람 코드를 보고 느낀 점

int dx[4] = {0, 0, 1, -1};
int dy[4] = {1, -1, 0, 0};

switch를 사용하지 않고 이동을 하는 방법
-> 그래프 문제 사용할 때 많이 쓰던 방법인데 여기서도 이렇게 사용할 수 있구나 싶기도 하고 오히려 복잡한 것 같기도 한 것 같다는 생각이 드네요.

    int cx, cy;
                tie(cx, cy) = {i, j};

변수로 선언한 것들을 tie로 묶어서 받는 모습, 굳이란 생각도 들지만 저런 방법이 있다는 게 신기하네요.

반응형
728x90

타임리프를 사용하다보면 자바 컨트롤러에서 던져주는 model 객체 내 값들을 JS에서 쓰고 싶은 욕구가 생긴다.

왜냐하면 Js에서 어떤 작업을 해야 하는데 필요한 값들이 페이지 내에 존재하는 경우가 많고, 이를 HTML 태그에서 갈무리를 해서 써야 한다. 그러면 document나 Jquery를 이용해서 HTML 내에 값을 가져와서 사용한다.

HTML 내 태그에 값을 숨겨 놓고 그것을 가져다 쓰는 작업이 꽤나 귀찮다. 

애초에 JS 변수 내에 값이 있으면 해결되는 문제가 아닐까?

그렇게 하는 방법이 있다.

package com.example.kg;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class KgController {

    @GetMapping("/hello")
    public String hello(Model model) {
        model.addAttribute("Hello", "hello");
        return "/kg";
    }
}

그냥 단순한 예제이니 model에 넣은 값만 확인하자.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
</head>
<body>
    <div th:text="${Hello}"></div>
</body>
<script th:inline="javascript" type="text/javascript">
    const hello = [[${Hello}]];
</script>
<script  type="text/javascript" th:src="@{/kg.js}"></script>
</html>

위처럼 inline에 넣어두면 된다.

여기서 th:inline을 사용하면 js 변수 내에 [[${ }]]로 가능하다.
이게 왜 좋냐면 th:inline을 사용하지 않으면 [[${}]] 양 옆에 /* */를 사용해야 한다.
그리고 외부 Js에서는 이렇게 사용할 수가 없다. 
그런 이유는 html을 렌더링? 서버에서 템플릿 작업을 해주면서 템플릿 언어에 해당하는 문법을 보고 작업을 해준다.

그렇기 때문에 html에 외부 js 파일이 내장되어 있지 않으니 정상적으로 변수에 값이 들어가지 않는 것이다.

그리고 th:inline이 없으면 템플릿 엔진이 이를 작업해주지 않는 것 같다.

? 그런데 /* */는 로 해주면 어떻게 읽어서 작업해주는건지 신기 하다.

중요한건 th:inline을 하고 /* */ 없이 JS에 값을 쓸 수 있다는 사실이다.

반응형
728x90

https://school.programmers.co.kr/learn/courses/30/lessons/131127

 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr

10일동안 원하는 제품을 모두 할인해서 구매해야한다.

unoredred_map의 사용법이 신기했던 문제다.

#include <string>
#include <vector>
#include <iostream>
#include <unordered_map>
#include <deque>
using namespace std;

int solution(vector<string> want, vector<int> number, vector<string> discount) {
    int answer = 0;
    
    deque<string> dq;
    unordered_map<string, int> m;
    unordered_map<string, int> origin;    
     
    for(int i=0; i<want.size(); i++) {
        origin.insert({want[i], number[i]});
    }
    
    for(int i=0; i<10; i++) {
        string temp = discount[i];
        dq.push_back(temp);
        m[temp]++;
    }
        
    if(m == origin) {
        answer++;
    }

    for(int i=10; i<discount.size(); i++) {
        m[dq.front()]--;
        dq.pop_front();
        m[discount[i]]++;
        dq.push_back(discount[i]);
        
        bool check = true;
        for(int j=0; j<want.size(); j++) {
            
            if(m[want[j]] < origin[want[j]]) {
                check = false;
                break;
            }
        }
        if(check) {
            answer++;
        }
    }
        
    return answer;
}

맵에 있는 값을 --처리하여 0이 되어도, map에서 사라지지 않는다.

그렇기 때문에 비교하는 for문이 하나 더 들어가게 된다.

erase를 써서 지워버린다면, for문이 사라지고 == 처리로 비교가 한번에 가능해진다.

하지만 내가 사고 싶은 물건보다 더 많이 할인하는 경우를 체크하지 못하지 않을까 싶지만 사실 상관없다.
=>  애매한 부분이라 정확한 설명은 못하지만, ==처리로 같은지 확인할 수있다는 점을 알게 되었다는 점에서 신기하다.

#include <string>
#include <vector>
#include <iostream>
#include <unordered_map>
#include <deque>
using namespace std;

int solution(vector<string> want, vector<int> number, vector<string> discount) {
    int answer = 0;
    
    deque<string> dq;
    unordered_map<string, int> m;
    unordered_map<string, int> origin;    
     
    for(int i=0; i<want.size(); i++) {
        origin.insert({want[i], number[i]});
    }
    
    for(int i=0; i<10; i++) {
        string temp = discount[i];
        dq.push_back(temp);
        m[temp]++;
    }
        
    if(m == origin) {
        answer++;
    }

    for(int i=10; i<discount.size(); i++) {
        m[dq.front()]--;
        if(m[dq.front()] == 0) {
            m.erase(dq.front());
        }
        dq.pop_front();
        m[discount[i]]++;
        dq.push_back(discount[i]);
        
        if(m == origin) {
            answer++;
        }
    }
        
    return answer;
}
반응형
728x90

https://koolreview.tistory.com/127

 

Spring Boot Security 5 - Oauth2.0 구글 로그인 (HTTP BASIC 탐방) - 1

단순하게 Spring Boot Security 5에 대해 코드만 작성하는 글이 아니라, 이해를 위한 글을 작성하기 노력하고 있습니다. Oauth2.0을 알아보기 전에 기본인 HTTP Basic 로그인에 대해 알아보겠습니다. 1. 종속

koolreview.tistory.com

이전 글에서 HTTP BASIC 로그인에 대해 알아보았습니다. 이번에는 HTTP OAUTH2.0로그인에 대해 알아보겠습니다.

1. 라이브러리 추가

implementation group: 'org.springframework.boot', name: 'spring-boot-starter-oauth2-client', version: '2.7.1'

 

반응형
728x90

단순하게 Spring Boot Security 5에 대해 코드만 작성하는 글이 아니라, 이해를 위한 글을 작성하기 노력하고 있습니다.

Oauth2.0을 알아보기 전에 기본인  HTTP Basic 로그인에 대해 알아보겠습니다.

1. 종속성 추가하기

먼저 종속성을 추가해줍니다. 기존 프로젝트에 시큐리티만 추가하면 됩니다.
lombok은 코드를 간편하게 쓰기 위해 추가했습니다.

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-security'
    implementation 'org.springframework.boot:spring-boot-starter-web'
    compileOnly 'org.projectlombok:lombok'
    annotationProcessor 'org.projectlombok:lombok'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
    testImplementation 'org.springframework.security:spring-security-test'
}

1-2. 시큐리티 추가만으로 생기는 보안

이렇게 라이브러리를 단순히 추가함으로 왼쪽(추가 전)에 되던 통신이 오른쪽처럼 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가 되어 다른 코드를 사용해야 한다. 자세한 내용은 아래 하단 링크를 통해 확인하면 된다.

https://spring.io/blog/2022/02/21/spring-security-without-the-websecurityconfigureradapter

 

Spring Security without the WebSecurityConfigurerAdapter

<p>In Spring Security 5.7.0-M2 we <a href="https://github.com/spring-projects/spring-security/issues/10822">deprecated</a> the <code>WebSecurityConfigurerAdapter</code>, as we encourage users to move towards a component-based security configuration.</p> <p

spring.io

- 고민해야하는 것, Bean? 메소드 재정의?

 시큐리티를 이용할 때 두 가지 방법을 사용할 수 있다. 둘 중 하나만으로 구현해야지 일관성이 있고, 코드가 헷갈리지 않는다. 

@EnableSecrurity 어노테이션

EnableWebSecurity Interface

클래스 밖에 Configuration 어노테이션을 붙일 필요가 없다. 이미 EnableWebSecurity 인터페이스 안에 우리가 사용하고자 하는 어노테이션이 있으니 Configuration 어노테이션을 붙일 필요없다.

6. HttpBasic 활성화

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {

        http.httpBasic();
        http.authorizeRequests()
            .anyRequest().authenticated();

        return http.build();
    }

https://docs.spring.io/spring-security/site/docs/current/api/

 

Overview (spring-security-docs 5.7.3 API)

Authentication processing mechanisms, which respond to the submission of authentication credentials using various protocols (eg BASIC, CAS, form login etc).

docs.spring.io

7. User Entity 생성

로그인 구현하기 위해 DB상에 user 테이블을 생성합니다. 생성하는 이유는 유저의 정보를 DB 상에 저장하기 위함입니다.
만약에 이를 이용하지 않는다면 메모리에 저장해두면 됩니다. 이 방법은 따로 이 글에선 알아보지 않겠습니다.

import javax.persistence.*;
import java.util.List;

@Entity
public class User {

    @Id
    @GeneratedValue
    private Integer id;

    private String username;
    private String password;

    @OneToMany(mappedBy = "user", fetch = FetchType.EAGER)
    private List<Authority> authorities;

    public List<Authority> getAuthorities() {
        return authorities;
    }

    public String getUsername() {
        return username;
    }

    public String getPassword() {
        return password;
    }

}

8.UserRepository 생성

FindUserByUsername 메소드를 하나 작성해줍니다. 작성 이유는 Username으로 값을 조회하기 위함입니다.

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import java.util.Optional;

@Repository
public interface UserRepository extends JpaRepository<User, Integer> {
    Optional<User> findUserByUsername(String username);
}

9. CustomUserDetail 생성

이 클래스를 생성하는 이유는 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를 구현합니다.

https://docs.spring.io/spring-security/site/docs/current/api/org/springframework/security/core/userdetails/UserDetails.html

권한, 비밀번호, 아이디 등에 관한 메소드를 정의하고 있습니다.

authentication 인터페이스는 아래 링크를 통해 확인할 수 있습니다.

https://docs.spring.io/spring-security/site/docs/current/api/org/springframework/security/authentication/package-summary.html

10. JpaUserService 생성

Jpa라고 붙은 이유는 Data Jpa 라이브러리를 통해서 디비 상에 값이 존재하는지 확인하기 때문입니다.
또한 이 클래스의 loadUserByUsername메소드는 위에서 정의한 CustomUserDeatils 객체를 반환합니다.

import com.example.demo.User;
import com.example.demo.UserRepository;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

import java.util.function.Supplier;

@Service
public class JpaUserDetailsService implements UserDetailsService {

    private final UserRepository userRepository;

    public JpaUserDetailsService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    @Override
    public CustomUserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        Supplier<UsernameNotFoundException> s = () -> new UsernameNotFoundException("Problem during authentication!");

        User user = userRepository.findUserByUsername(username).orElseThrow(s);

        return new CustomUserDetails(user);
    }

}

11. AuthenticationProviderService 생성

상단 부분을 다시 읽어보면, Provider가 무슨 일을 하는 지 알 수 있습니다. 다음과 같습니다.

3. 인증  공급자 (Authentication Provider):
     -  사용자 관리 책임을 구현하는 사용자 세부 정보 서비스를 인증 논리에 이용한다.
     -  암호 관리를 구현하는 암호 인코더를 인증 논리에 이용한다.

결국 인증한다는 사실만 기억하세요!

import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

@Service
public class AuthenticationProviderService implements AuthenticationProvider {

    private final JpaUserDetailsService userDetailsService;

    private final BCryptPasswordEncoder bCryptPasswordEncoder;

    public AuthenticationProviderService(JpaUserDetailsService userDetailsService, BCryptPasswordEncoder bCryptPasswordEncoder) {
        this.userDetailsService = userDetailsService;
        this.bCryptPasswordEncoder = bCryptPasswordEncoder;
    }

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {

        String username = authentication.getName();
        String password = authentication.getCredentials().toString();

        CustomUserDetails user = userDetailsService.loadUserByUsername(username);

        return checkPassword(user, password, bCryptPasswordEncoder);
    }

    @Override
    public boolean supports(Class<?> authentication) {
        return UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication);
    }

    public Authentication checkPassword(CustomUserDetails user, String rawPassword, PasswordEncoder encoder) {
        if(encoder.matches(rawPassword, user.getPassword())) {
            return new UsernamePasswordAuthenticationToken(user.getUsername(), user.getPassword(), user.getAuthorities());
        } else {
            throw new BadCredentialsException("Bad credentials");
        }
    }
}

12. 설정

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
@EnableWebSecurity
public class ProjectConfig {

    private final AuthenticationProviderService authenticationProvider;

    public ProjectConfig(AuthenticationProviderService authenticationProvider) {
        this.authenticationProvider = authenticationProvider;
    }

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {

        http.httpBasic();

        http.authorizeRequests()
            .anyRequest().authenticated();

        http.authenticationProvider(authenticationProvider);

        http.formLogin()
                .defaultSuccessUrl("/main", true);

        return http.build();
    }

    @Bean
    public BCryptPasswordEncoder bCryptPasswordEncoder() {
        return new BCryptPasswordEncoder();
    }

}

AuthenticationProvder도 설정해주시면 됩니다.

이정도면 HTTP BASIC은 성공입니다.

다음 글에서는 Oauth2.0 로그인을 알아보겠습니다.

반응형
728x90

 

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

 

2668번: 숫자고르기

세로 두 줄, 가로로 N개의 칸으로 이루어진 표가 있다. 첫째 줄의 각 칸에는 정수 1, 2, …, N이 차례대로 들어 있고 둘째 줄의 각 칸에는 1이상 N이하인 정수가 들어 있다. 첫째 줄에서 숫자를 적절

www.acmicpc.net

 

이 글을 읽는다면 문제는 이미 다 알고 있다고 생각합니다.

구해야하는 정답은 첫 번째 줄에서 뽑은 수와  두 번째에서 뽑은 수가 같은 집합의 최대 크기이다.

두번째 줄에서 주어진 수에서 인덱스번호가 없는 2와 7은 첫번째 줄에서 선택할 필요가 없다.
-> 왜냐하면 2와 7을 뽑아봤자 2번째 줄에 2와 7이 없기 때문에 같은 수를 뽑을 수가 없기 때문이다.

일단 이해하기 쉽도록 그래프를 그려줍니다.
-> 그래프를 그릴 때 각 노드들은 인덱스 번호이며 연결되어있는 값들은 2번째 줄에서 준 값들의 인덱스 번호입니다.

그럼으로 2와 7을 기준으로  각 노드들에 연결되어 있는 2와 7를 제거합니다.

## 먼저 2를 제거합니다

2가 연결된 곳은 1 번 노드였고 1번 노드에 자식 노드가 있음으로 제거할 대상이 아닙니다. 이후 2가 연결된 곳이 없음으로 7를 제거합니다.

## 7 제거

7이 연결된 곳은 6이었고, 지우니, 자식 노드가 없습니다. 이로 인해 6도 사용하지 못합니다. 그럼으로 6도 지워줍니다.

## 6 제거

6이 연결된 곳은 4였고 지우니, 자식 노드가 없으므로 쓰지 못합니다.

## 4 제거

4가 연결된 곳은 5였고 제거하니 자식 노드가 남아있음으로 제거를 멈춥니다.

다 제거하고 나니  사용가능한 수는 총 3개입니다.

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

int inputs[101];
vector<int> adj[101];

int main() {
    int n;
    cin >> n;

    for(int i=1; i<=n; i++) {
        cin >> inputs[i];
        adj[inputs[i]].push_back(i);
    }

    int answer = n;
    for(int i=1; i<=n; i++) {
        if(adj[i].size() == 0) {
            
            int k = i;
            int index = inputs[i];
            
            while(true) {
                // 제거
                for(int j=0; j<adj[index].size(); j++) {
                    if(adj[index][j] == k) {
                        adj[index].erase(adj[index].begin()+ j);
                        answer--;
                        break;
                    }
                }
                
                if(adj[index].size() == 0) {
                    k = index;
                    index = inputs[index];
                    
                } else {
                    break;
                }
            }
        }
    }

    vector<int> answers;
    for(int i=1; i<=n; i++) {
        if(adj[i].size() != 0 ) {
            for(int j=0; j<adj[i].size(); j++) {
                answers.push_back(adj[i][j]);
            }
        }
    }
    sort(answers.begin(), answers.end());

    cout << answer << "\n";
    for(int i=0; i<answers.size(); i++){
        cout << answers[i] << "\n";
    }
}

## 재귀로 작성

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

int inputs[101];
vector<int> adj[101];
int answer;
int n;

void remove(vector<int>& temp, int target){
    for(int j=0; j<temp.size(); j++) {
        if(temp[j] == target) {
            temp.erase(temp.begin()+ j);
            answer--;
            break;
        }
    }
}

void recrucive(int target, int index) {
    
    remove(adj[index], target);
            
    if(adj[index].size() == 0) {
        recrucive(index, inputs[index]);
    } else {
        return;
    }

}

int main() {
    cin >> n;
    answer = n;
    for(int i=1; i<=n; i++) {
        cin >> inputs[i];
        adj[inputs[i]].push_back(i);
    }

    for(int i=1; i<=n; i++) {
        if(adj[i].size() == 0) {
            recrucive(i, inputs[i]);
        }
    }

    vector<int> answers;
    for(int i=1; i<=n; i++) {
        if(adj[i].size() != 0 ) {
            for(int j=0; j<adj[i].size(); j++) {
                answers.push_back(adj[i][j]);
            }
        }
    }
    sort(answers.begin(), answers.end());

    cout << answer << "\n";
    for(int i=0; i<answers.size(); i++){
        cout << answers[i] << "\n";
    }
}

 

반응형

'코딩 관련 > c++' 카테고리의 다른 글

공원 산책 프로그래머스  (0) 2023.06.21
프로그래머스 할인행사  (0) 2022.12.17
백준 1, 2, 3 더하기 5 문제 분석  (0) 2022.08.09
이집트인의 곱셈  (0) 2021.08.24
스택 응용 분석해보기 1편  (0) 2021.08.24
728x90

일단 문제를 확인해보면 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의 수를 더하면 된다.

근데 이 문제는 연속된 경우를 피해야 한다. 어떻게 피할 수 있을까? 일단 일차원 배열의 경우, 안에 중첩된 경우를 분간하지 못한다.

# 느낌

3차원으로 dp 그리기

# 1의 경우

모양이 똑같다.

# 2의 경우

# 3의 경우

# 4의 경우

근데 표를 돌려본다면?

테이블이 트리 모양이랑 똑같다.

신기하다

 

반응형

'코딩 관련 > c++' 카테고리의 다른 글

프로그래머스 할인행사  (0) 2022.12.17
백준 C++ 숫자고르기 골5 문제 풀이  (0) 2022.08.15
이집트인의 곱셈  (0) 2021.08.24
스택 응용 분석해보기 1편  (0) 2021.08.24
자료구조 Stack 구현하기  (0) 2021.08.23
728x90

 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.client-id = 
spring.security.oauth2.client.registration.twitch.client-secret = 
spring.security.oauth2.client.registration.twitch.client-name=Twitch
spring.security.oauth2.client.registration.twitch.authorization-grant-type = authorization_code
spring.security.oauth2.client.registration.twitch.client-authentication-method=post
spring.security.oauth2.client.registration.twitch.redirect-uri = http://localhost:8080/login/oauth2/code/twitch
spring.security.oauth2.client.registration.twitch.response_type=token
spring.security.oauth2.client.registration.twitch.scope=user:read:email

# Twitch Provider 등록
spring.security.oauth2.client.provider.twitch.authorization-uri=https://id.twitch.tv/oauth2/authorize
spring.security.oauth2.client.provider.twitch.token-uri=https://id.twitch.tv/oauth2/token
spring.security.oauth2.client.provider.twitch.user-info-uri=https://id.twitch.tv/oauth2/userinfo
spring.security.oauth2.client.provider.twitch.user-name-attribute=token

 2. 기본 지식의 부족

 code를 받아서 token를 받아오는 방식이 있다. => 권한 코드

브라우저 기반 클라이언트 사이드 앱 암묵적 허가 => 액세스 토큰 발급

총 4가지 방식 중에서 2가지를 헷갈렸었다.

권한 코드는 백엔드에서 처리하는 방식에 적합하고 위 설정 파일에서spring.security.oauth2.client.registration.twitch.authorization-grant-type = authorization_code 라고 적은 부분도 어떤 방식을 이용하겠다고 설정한 것이다.

방법의 종류와 방법의 진행 방식을 책을 통해서 학습 후 진행하니까 수월해졌다.
 

반응형

+ Recent posts