반응형

HOC(Higher Order Component)

: 다른 컴포넌트를 받은 다음에, 새로운 컴포넌트를 반환한다.

: 받은 컴포넌트가 인증된 것만 새로운 컴포넌트를 반환해준다.

 

만약, 이미 로그인한 상태였다면 로그인 페이지가 아닌 다른 페이지를 반환해줘야 한다.

 

 

1. auth.js 에서 로그인한 상태로는 들어갈 수 없거나, 관리자 권한 없이는 들어가지 못하는 경우를 처리해준다.

 

만약 로그인하지 않았는데 option이 true라 로그인한 유저만 출입가능하다면,

로그인 페이지로 이동

 

만약 로그인하였는데 관리자만 들어갈 수 있는 페이지이고, 나는 관리자가 아니라면

landing page로 이동

 

만약 로그인하였는데 option이 false라서 로그인한 유저는 출입 불가능이라면

landing page로 이동

 

import React, {useEffect} from 'react';
import { useDispatch } from 'react-redux';
import { auth } from '../_actions/user_action';

export default function ( SpecificComponent, option, adminRoute = null ){       // adminRoute 는 관리자만 들어갈수있는

    // option //
    // null : 아무나 출입가능
    // true : 로그인한 유저만 출입가능
    // false : 로그인한 유저는 출입불가능

    function AuthenticationCheck(props){

        const dispatch = useDispatch();
        

        useEffect(() => {
            
            dispatch(auth()).then(response => {
                console.log(response);

                // 로그인 하지 않은 상태
                if(!response.payload.isAuth){
                    if(option){
                        props.history.push('/login');
                    }
                } else {
                    // 로그인 한 상태
                    if(adminRoute && !response.payload.isAdmin){
                        props.history.push('/');
                    } else {
                        if(option === false){
                            props.history.push('/');
                        }
                    }
                }
            })

            
        }, [])

        return (
            <SpecificComponent />
        )
    }
    return AuthenticationCheck
}

auth.js

 

 

반응형
반응형

1. Spring와 Node.js

Spring은 Java를 이용하는 프레임워크이고, Node.js는 Javascript 런타임이다. 둘 다 서버 개발을 할 수 있다.

2. 차이점 

2-1. Spring

장점

  • 크기와 부하의 측면에서 경량이다.
  • Spring이 안정적이라는 말을 많이 한다. 그 이유는 레퍼런스가 많아 이슈를 쉽게 해결할 수 있고, 자유성이 낮다는 점에서 안정적이라고 생각한다.
  • 스프링은 2002년 로드 존슨이 최초로 개발하였기에 Spring을 다루는 개발자가 많아 인력 문제도 없다.
  • 많은 개발자들이 오랫동안 사용하고 유지해왔기 때문에 알려진 버그와 보안 문제는 많이 잡혀있어서 안정성이 우수하다.
  • 정형화된 패턴이 있다.
  • IOC/ DI로 느슨한 결합도를 유지한다.
  • 자유도는 낮지만 안정적이다.

단점

  • 설정이 복잡한 편이다.

 

 

 

2-2. node.js

장점

  • V8 엔진을 기반으로 한 성능이 뛰어나다.
  • 싱글스레드, 비동기 IO 처리에 기반한 빠른 속도를 가진다.
  • 파일 I/O나 네트워크 처리를 이벤트 드리븐 방식으로 처리하기 때문에 빠른 처리가 가능하다.
  • CPU의 대기시간을 최소화 할 수 있다.
  • CPU 부하가 적고, 많은 커넥션을 동시에 처리해야 하는 구조에 적합하다.
  • 자바스크립트를 이용해서 개발할 수 있기 때문에 프론트엔드 개발자의 진입장벽이 낮다.
  • 기존 Java 서버에 비해 생산성이 훨씬 높다.

단점

  • 싱글스레드 모델이기 때문에 하나의 작업에 시간이 오래걸리면 시스템 전체의 성능이 급격하게 떨어진다.
  • 에러가 발생할 경우 프로세스 자체가 죽어버리므로 주의해야한다.(watch dog 등으로 처리 가능)
  • 멀티코어 활용을 위해서 cluster 모듈을 이용해야 하고, 세션을 공유할 경우 부가적인 작업이 필요하다.
  • 비동기 제어가 까다롭다.

 

 

 

 

3. 주요특징

Spring MVC의 다중요청 처리

Spring MVC는 Thread를 여러개 두어 (Multi-Thread) 다중요청을 동시에 처리합니다. 각 요청마다 별개의 Thread가 해당 요청을 응답까지 책임지는 구조입니다. 

 

마치 식당에서 여러명의 웨이터(server)가 여러 손님(client)들의 요청을 하나씩 분담하는 구조입니다. 이러한 방식에서 많은 웨이터를 두면 손쉽게(?) 많은 손님들의 요청을 동시에 처리할 수 있습니다. 다만, 일반적인 식당에서는 웨이터가 손님을 직접적으로 대응하는 시간보다 주방(kitchen)에서 음식을 조리하는 시간이 더 오래 걸리기 마련입니다. 주방에서 조리하는 시간에 웨이터들은 음식이 나오기만을 하염없이 기다린다면 식당 주인 입장에서 자원의 낭비가 수반되는 방식입니다.

 

Spring MVC의 Multi-Thread방식에서 I/O 작업이 수반된다면 위의 예제와 같은 자원의 낭비가 발생됩니다. Thread가 수행하는 요청과 응답에 대한 일처리에 비하여, I/O 작업은 훨씬 긴 작업시간을 가지고 있고 이러한 시간동안 Thread들이 블로킹되기 때문입니다. Thread의 갯수가 증가될때마다 컨텍스트 스위칭이 빈번하게 일어나게 될 것이고 이로 인한 성능 저하도 야기 될 수 있습니다. 

 

 

 

Node.js의 다중요청 처리

반면에 Node.js는 단 한개의 Thread로 (Single-Thread) 모든 요청을 처리합니다.

 

한개의 Thread만으로도 Spring MVC의 처리속도와 비견될 수 있는 이유는 Node.js가 완전한 비동기-논블러킹 방식의 I/O 작업을 수용하기 때문입니다. 상대적으로 긴 시간이 소요되는 I/O작업을 기다리지 않고 client의 요청과 응답만을 담당하기 때문에 더 적은 자원으로도 높은 성능을 기대할 수 있습니다. (사실 Single-Thread 방식은 Node.js의 특징이라기 보다는 Javascript의 특징이라고 보는게 맞습니다.)

 

+ 추가적으로 단 한개의 Thread만이 운용되기 때문에 기존 Multi-Thread 방식의 동기화 문제에서 완전히 자유롭습니다. 이로 인한 개발편의성의 이점도 얻을 수 있습니다.

 

 

 

 

그래서 Node.js가 Spring MVC보다 성능상 유리할까?

Node.js가 Spring MVC보다 성능상 우위에 있다라고 말하기 위해서 몇가지 전제사항이 있습니다.

 

1. 다중처리를 동시에 처리하도록 요구된다.

2. 많은 I/O 작업을 수행한다.

3. 단순한 CPU 작업만을 진행한다.

 

이같은 전제사항은 오늘날의 '전형적인' 웹 애플리케이션의 특징입니다. Node.js가 다른 웹프레임워크에 비해서 성능상 우위에 있다고 쉽게 거론되는 이유는 Node.js가 이러한 '전형적인' 웹 애플리케이션의 요구사항과 잘 어우러지기 때문입니다.

 

하지만 이러한 범주에서 벗어난 웹 애플리케이션이라면 반드시 Node.js가 정답이 될 수 없습니다. I/O작업이 많지 않고 강도높은 CPU 연산이 요구된다면 한개의 Thread를 사용하는 Node.js는 급격한 성능 저하가 야기됩니다.

 

Spring WebFlux의 등장

(Java 진영은 언제나 그래왔듯이 정답을 찾을 것입니다.)

 

Spring WebFlux는 Spring 5에서 도입되었습니다. Spring WebFlux는 Node.js와 유사하게 완전한 비동기-논블러킹 방식의 I/O 작업이 가능하고, 이를 제어하기 위한 Event Loop를 두었습니다. 다른 점이라면 Spring WebFlux는 Single-Thread 기반이 아닌 Multi-Thread 방식입니다. 다만 Spring MVC처럼 각 요청에 하나의 Thread가 대응 되지 않고 다수의 요청을 처리하는 구조입니다.

 

Java진영에서도 비동기-논블러킹 방식의 장점을 취하기 위해서 Node.js를 고려하지 않고 Spring 만으로도 개발이 가능하게 된 것입니다. 

반응형
반응형

1. 전체 프로세스 <로그인>

회원가입 - 이메일 - 이름 - 비밀번호 - 비밀번호확인 - 확인

 

 

2. RegisterPage 도 LoginPage 와 비슷한 맥락으로 만들어주기

 

import React, {useState} from 'react'
import {useDispatch} from 'react-redux';
import {registerUser} from '../../../_actions/user_action'

function RegisterPage(props) {

    const dispatch = useDispatch();

    const [Email, setEmail] = useState("")
    const [Password, setPassword] = useState("")
    const [Name, setName] = useState("")
    const [ConfirmPassword, setConfirmPassword] = useState("")

    const onEmailHandler = (event) => {
        setEmail(event.currentTarget.value)
    } 
    const onNameHandler = (event) => {
        setName(event.currentTarget.value)
    }
    const onPasswordHandler = (event) => {
        setPassword(event.currentTarget.value)
    }
    const onConfirmPasswordHandler = (event) => {
        setConfirmPassword(event.currentTarget.value)
    }

    const onSubmitHandler = (event) => {
        event.preventDefault()

        if(Password !== ConfirmPassword){
            return alert('비밀번호와 비밀번호 확인은 같아야 합니다.');
        }

        let body = {
            email: Email,
            password: Password,
            name: Name
        }

        dispatch(registerUser(body))
        .then(response => {
            if(response.payload.success) {
                props.history.push('/login');
            } else {
                alert("Failed to sign up")
            }
        })
    }

    return (
        <div style={{
            display: 'flex', justifyContent: 'center', alignItems: 'center',
            width: '100%', height: '100vh'
        }}>
            <form style={{ display: 'flex', flexDirection: 'column'}}
                onSubmit={onSubmitHandler}
            >
                <label>Email</label>
                <input type="email" value={Email} onChange={onEmailHandler}/>
                
                <label>Name</label>
                <input type="text" value={Name} onChange={onNameHandler}/>

                <label>Password</label>
                <input type="password" value={Password} onChange={onPasswordHandler}/>

                <label>Confirm Password</label>
                <input type="password" value={ConfirmPassword} onChange={onConfirmPasswordHandler}/>

                <br />
                <button>
                    회원가입
                </button>
            </form>
        </div>
    )
}

export default RegisterPage

RegisterPage.js

 

 

 

3. user_action에 registerUser 추가

 

import {
    LOGIN_USER,
    REGISTER_USER
} from './types';

export function registerUser(dataTosubmit){
    const request = axios.post('/api/users/register', dataTosubmit)
        .then(response => response.data)

    return {
        type: REGISTER_USER,
        payload: request
    }
}

 

 

 

4. types.js 에도 추가

 

export const REGISTER_USER = "register_user";

 

 

 

 

5. user_reducers 에 추가

 

import {
    LOGIN_USER, REGISTER_USER
} from '../_actions/types';

case REGISTER_USER:
            return { ...state, register: action.payload };

 

 

 

 

 

<로그아웃>

- 로그아웃 버튼 만들어주기

const onClickHandler = () => {
        axios.get('/api/users/logout')
        .then(response => {
            if(response.data.success){
                props.history.push('/login');
            } else {
                alert('로그아웃 하는 데 실패하였습니다.');
            }
        })
    }

LandingPage.js

 

+ props 넣어주기

반응형
반응형

⭕ DFS 장점

  • 현 경로상의 노드를 기억하기 때문에 적은 메모리를 사용합니다.
  • 찾으려는 노드가 깊은 단계에 있는 경우 BFS 보다 빠르게 찾을 수 있습니다.

❌ DFS 단점

  • 해가 없는 경로를 탐색 할 경우 단계가 끝날 때까지 탐색합니다. 효율성을 높이기 위해서 미리 지정한 임의 깊이까지만 탐색하고 해를 발견하지 못하면 빠져나와 다른 경로를 탐색하는 방법을 사용합니다.
  • DFS를 통해서 얻어진 해가 최단 경로라는 보장이 없습니다. DFS는 해에 도착하면 탐색을 종료하기 때문입니다.

 
 

⭕ BFS 장점

  • 답이 되는 경로가 여러 개인 경우에도 최단경로임을 보장한다.
  • 최단 경로가 존재하면 깊이가 무한정 깊어진다고 해도 답을 찾을 수 있다.

❌ BFS 단점

  • 경로가 매우 길 경우에는 탐색 가지가 급격히 증가함에 따라 보다 많은 기억 공간을 필요로 하게 된다.
  • 해가 존재하지 않는다면 유한 그래프(finite graph)의 경우에는 모든 그래프를 탐색한 후에 실패로 끝난다.
  • 무한 그래프(infinite graph)의 경우에는 결코 해를 찾지도 못하고, 끝내지도 못한다.

(Socurce: Wiki)
 
 
 
 
 

🌺 DFS vs BFS 탐색 차이

Figure 2. 트리에서 DFS, BFS 탐색 차이

  • DFS는 스택(혹은 재귀), BFS 큐를 사용합니다.
  • BFS는 재귀적으로 동작하지 않습니다.

But 문제를 푸는 입장에서는 다음과 같은 구분점이 필요합니다.

  • 최단 거리 문제를 푼다면 BFS를 사용합니다.
  • 이동할 때마다 가중치가 붙어서 이동한다거나 이동 과정에서 여러 제약이 있을 경우 DFS로 구현하는 것이 좋습니다.
반응형
반응형

Reference Code

백준 2178 - 미로 탐색

www.acmicpc.net/problem/2178

 

2178번: 미로 탐색

첫째 줄에 두 정수 N, M(2 ≤ N, M ≤ 100)이 주어진다. 다음 N개의 줄에는 M개의 정수로 미로가 주어진다. 각각의 수들은 붙어서 입력으로 주어진다.

www.acmicpc.net

 

 

#include <iostream>
#include <stdio.h>
#include <queue>
#include <stack>
#include <algorithm>

#define MAX_SIZE 101

using namespace std;

// 위, 오른, 아래, 왼
int dx[] = {-1, 0, 1, 0};
int dy[] = {0, 1, 0, -1};

int n, m; // 행과 열의 수
int dis[MAX_SIZE][MAX_SIZE];

int map[MAX_SIZE][MAX_SIZE]; // 지도
bool visited[MAX_SIZE][MAX_SIZE]; // 방문했는지를 표시하는 지도


// BFS
void bfs(int x, int y){
  
    queue< pair<int,int> > q; // 이용할 큐, (x,y) -> (행, 열)
    q.push(make_pair(x,y)); // pair를 만들어서 queue에 넣습니다.

    // 처음 x,y를 방문 했기때문에
    visited[x][y] = true;

    while(!q.empty()){

        // 큐의 현재 원소를 꺼내기
        x = q.front().first;
        y = q.front().second;
        q.pop();

        // 해당 위치의 주변을 확인
        for(int i = 0; i < 4; i++){
            int nx = x + dx[i];
            int ny = y + dy[i];

            // 지도를 벗어나지 않고
            if(0 <= nx && nx < n && 0 <= ny && ny < m){
                // 집이면서 방문하지 않았다면 -> 방문
                if(map[nx][ny] == 1 && visited[nx][ny] == false){
                    visited[nx][ny]=true;

                    // 해당 단지의 집의 수를 증가시킴
                    dis[nx][ny] = dis[x][y] + 1;

                    // 큐에 현재 nx,ny를 추가
                    q.push(make_pair(nx,ny));
                }
            }
        }
    }
}

int main (){

    scanf("%d%d", &n, &m);

    for(int i = 0; i < n; i++){
        for(int j = 0; j < m; j++)
            //입력을 1개씩 숫자로 끊어서 받습니다 -> %1d
            scanf("%1d", &map[i][j]);
    }
    
    dis[0][0] = 1;
    bfs(0, 0);

    printf("%d\n",dis[n-1][m-1]);
}

 

map, visited 2차원

queue 1차원 pair 로 구현

 

 

 

 

 

 

 

 

 

응용문제)

백준 2667 - 단지 번호 붙이기

www.acmicpc.net/problem/2667

 

#include <iostream>
#include <stdio.h>
#include <queue>
#include <stack>
#include <algorithm>

#define MAX_SIZE 25

using namespace std;

// 위, 오른, 아래, 왼
int dx[] = {-1, 0, 1, 0};
int dy[] = {0, 1, 0, -1};

int n; // 행과 열의 수
int group_id; // 단지의 번호로 첫번째 단지부터 1로 시작
int groups[MAX_SIZE * MAX_SIZE]; // 각 단지별 집의 수

int map[MAX_SIZE][MAX_SIZE]; // 지도
bool visited[MAX_SIZE][MAX_SIZE]; // 방문했는지를 표시하는 지도


// BFS
void bfs(int x, int y){

    queue< pair<int,int> > q; // 이용할 큐, (x,y) -> (행, 열)
    q.push(make_pair(x,y)); // pair를 만들어서 queue에 넣습니다.

    // 처음 x,y를 방문 했기때문에
    visited[x][y] = true;
    groups[group_id]++;  

    while(!q.empty()){

        // 큐의 현재 원소를 꺼내기
        x = q.front().first;
        y = q.front().second;
        q.pop();

        // 해당 위치의 주변을 확인
        for(int i = 0; i < 4; i++){
            int nx = x + dx[i];
            int ny = y + dy[i];

            // 지도를 벗어나지 않고
            if(0 <= nx && nx < n && 0 <= ny && ny < n){
                // 집이면서 방문하지 않았다면 -> 방문
                if(map[nx][ny] == 1 && visited[nx][ny] == false){
                    visited[nx][ny]=true;

                    // 해당 단지의 집의 수를 증가시킴
                    groups[group_id]++;

                    // 큐에 현재 nx,ny를 추가
                    q.push(make_pair(nx,ny));   
                }
            }
        }
    }
}



int main (){

    scanf("%d", &n);

    for(int i = 0; i < n; i++){
        for(int j = 0; j < n; j++)
            //입력을 1개씩 숫자로 끊어서 받습니다 -> %1d
            scanf("%1d", &map[i][j]);
    }

    // 전체 지도 탐색
    for(int i = 0; i < n; i++){
        for(int j = 0; j < n; j++){
            // 집이면서 방문하지 않았다면 -> 방문
            if(map[i][j]==1 && visited[i][j]==false){

                // 해당 지역에 단지 id를 부여하고
                group_id++;

                // 탐색
                bfs(i, j);
            }
        }
    }

    // 단지마다 집들의 수로 오름차순 정렬
    // ID는 1부터 시작
    // 함수 사용법: https://twpower.github.io/71-use-sort-and-stable_sort-in-cpp
    sort(groups + 1, groups + group_id + 1);

    printf("%d\n", group_id);
    for (int i = 1; i <= group_id; i++) {
        printf("%d\n", groups[i]);
    }
}

 

 

 

 

 

 

응용문제) 백준 2468 안전영역

www.acmicpc.net/problem/2468

 

2468번: 안전 영역

재난방재청에서는 많은 비가 내리는 장마철에 대비해서 다음과 같은 일을 계획하고 있다. 먼저 어떤 지역의 높이 정보를 파악한다. 그 다음에 그 지역에 많은 비가 내렸을 때 물에 잠기지 않는

www.acmicpc.net

 

#include <iostream>
#include <stdio.h>
#include <queue>
#include <stack>
#include <algorithm>
#include <cstring>  // memset

#define MAX_SIZE 101

using namespace std;

// 위, 오른, 아래, 왼
int dx[] = {-1, 0, 1, 0};
int dy[] = {0, 1, 0, -1};

int n; // 행과 열의 수

int map[MAX_SIZE][MAX_SIZE]; // 지도
bool visited[MAX_SIZE][MAX_SIZE];

// BFS
void bfs(int x, int y, int depth){
  
    queue< pair<int,int> > q; // 이용할 큐, (x,y) -> (행, 열)
    q.push(make_pair(x,y)); // pair를 만들어서 queue에 넣습니다.

    // 처음 x,y를 방문 했기때문에
    visited[x][y] = true;

    while(!q.empty()){

        // 큐의 현재 원소를 꺼내기
        x = q.front().first;
        y = q.front().second;
        q.pop();

        // 해당 위치의 주변을 확인
        for(int i = 0; i < 4; i++){
            int nx = x + dx[i];
            int ny = y + dy[i];

            // 지도를 벗어나지 않고
            if(0 <= nx && nx < n && 0 <= ny && ny < n){
                // 집이면서 방문하지 않았다면 -> 방문
                if(map[nx][ny] > depth && visited[nx][ny] == false){
                    visited[nx][ny]=true;

                    // 큐에 현재 nx,ny를 추가
                    q.push(make_pair(nx,ny));
                }
            }
        }
    }
}

int main (){
    int max = -1;
    int cnt[MAX_SIZE] = {0};

    scanf("%d", &n);

    for(int i = 0; i < n; i++){
        for(int j = 0; j < n; j++){
            scanf("%d", &map[i][j]);
            if(map[i][j] > max)
                max = map[i][j];
        }
    }
    
    for(int d=0; d<=max; d++){
        // visited 초기화
        memset(visited, false, sizeof(visited));
        
        for(int i=0; i<n; i++){
            for(int j=0; j<n; j++){
                if(map[i][j] > d && visited[i][j] == false){
                    cnt[d]++;
                    bfs(i, j, d);
                }
            }
        }
    }
    
    int cnt_max = -1;
    int cnt_max_id = -1;
    for(int i=0; i<=max; i++){
        if(cnt[i] > cnt_max){
            cnt_max_id = i;
            cnt_max = cnt[i];
        }
    }
    
    printf("%d\n",cnt[cnt_max_id]);
}
 

 

 

 

- 제일 빠른 경로를 찾는 것이 아니므로 dis 배열을 사용하지 않았다.

- map == 1 인 기준이 아니라, map > depth 기준으로 변경하였다.

- bfs를 여러 번 시행하여 가장 안전 영역이 큰 경우의 수를 찾았다.

- 전형적인 지도 유형의 문제

 

 

 

 

 

 

응용문제) 백준 1697 숨바꼭질

www.acmicpc.net/problem/1697

 

1697번: 숨바꼭질

수빈이는 동생과 숨바꼭질을 하고 있다. 수빈이는 현재 점 N(0 ≤ N ≤ 100,000)에 있고, 동생은 점 K(0 ≤ K ≤ 100,000)에 있다. 수빈이는 걷거나 순간이동을 할 수 있다. 만약, 수빈이의 위치가 X일

www.acmicpc.net

 

#include <iostream>
#include <stdio.h>
#include <queue>
#include <stack>
#include <algorithm>

#define MAX_SIZE 100001

using namespace std;

int n, m; // 수빈, 동생 위치

int dis[MAX_SIZE];
bool visited[MAX_SIZE]; // 방문했는지를 표시하는 배열

// BFS
void bfs(int x){
  
    queue <int> q; // 이용할 큐
    q.push(x);
    int nx;

    // 처음 x를 방문 했기때문에
    visited[x] = true;

    while(!q.empty()){

        // 큐의 현재 원소를 꺼내기
        x = q.front();
        q.pop();

        // 해당 위치의 주변을 확인
        nx = x * 2;
        
        // 지도를 벗어나지 않고, 방문한 적 없으면
        if(nx <= MAX_SIZE-1 && visited[nx] == false){
            visited[nx]=true;

            // 해당 단지의 집의 수를 증가시킴
            dis[nx] = dis[x] + 1;
            
            if(nx == m){
                printf("%d\n", dis[nx]);
                return;
            }

            // 큐에 현재 nx를 추가
            q.push(nx);
        }
        
        // 해당 위치의 주변을 확인
        nx = x + 1;
        
        // 지도를 벗어나지 않고, 방문한 적 없으면
        if(nx <= MAX_SIZE-1 && visited[nx] == false){
            visited[nx]=true;

            // 해당 단지의 집의 수를 증가시킴
            dis[nx] = dis[x] + 1;
            
            if(nx == m){
                printf("%d\n", dis[nx]);
                return;
            }

            // 큐에 현재 nx를 추가
            q.push(nx);
        }
        
        // 해당 위치의 주변을 확인
        nx = x - 1;
        
        // 지도를 벗어나지 않고, 방문한 적 없으면
        if(0 <= nx && visited[nx] == false){
            visited[nx]=true;

            // 해당 단지의 집의 수를 증가시킴
            dis[nx] = dis[x] + 1;
            
            if(nx == m){
                printf("%d\n", dis[nx]);
                return;
            }

            // 큐에 현재 nx를 추가
            q.push(nx);
        
        }
    }
}

int main (){
    
    scanf("%d%d", &n, &m);
    
    if(n == m){
        printf("0\n");
    } else {
        dis[n] = 0;
        bfs(n);
    }
    
}

 

 

- 지도 유형은 아니지만, 지도 레퍼런스를 가지고 풀었다.

- 2차원 배열을 모두 1차원으로 변경

- 세세한 예외 조건들을 설정하는게 까다로운 문제

 

 

 

 

 

 

 

 

응용문제) 백준 5014 스타트링크

 

#include <iostream>
#include <stdio.h>
#include <queue>
#include <stack>
#include <algorithm>

#define MAX_SIZE 10000001

using namespace std;

int f, s, g, u, d;

int dis[MAX_SIZE];
bool visited[MAX_SIZE]; // 방문했는지를 표시하는 배열

// BFS
void bfs(int x, int up, int down){
  
    queue <int> q; // 이용할 큐
    q.push(x);
    int nx;

    // 처음 x를 방문 했기때문에
    visited[x] = true;

    while(!q.empty()){

        // 큐의 현재 원소를 꺼내기
        x = q.front();
        q.pop();

        // 해당 위치의 주변을 확인
        nx = x + up;
        
        // 지도를 벗어나지 않고, 방문한 적 없으면
        if(nx >= 1 && nx <= f && visited[nx] == false){
            
            visited[nx]=true;

            // 해당 단지의 집의 수를 증가시킴
            dis[nx] = dis[x] + 1;
            
            if(nx == g){
                printf("%d\n", dis[nx]);
                return;
            }

            // 큐에 현재 nx를 추가
            q.push(nx);
        }
        
        // 해당 위치의 주변을 확인
        nx = x - down;
        
        // 지도를 벗어나지 않고, 방문한 적 없으면
        if(nx >= 1 && nx <= f && visited[nx] == false){
            
            visited[nx]=true;

            // 해당 단지의 집의 수를 증가시킴
            dis[nx] = dis[x] + 1;
            
            if(nx == g){
                printf("%d\n", dis[nx]);
                return;
            }

            // 큐에 현재 nx를 추가
            q.push(nx);
        }
    }
    printf("use the stairs\n");
}

int main (){
    
    scanf("%d%d%d%d%d", &f, &s, &g, &u, &d);
    
    if(s == g){
        printf("0\n");
    } else {
        bfs(s, u, d);
    }
    
}

 

 

 

- 숨바꼭질 문제를 변형해서 작성하였다.

- 역시나 예외 처리에서 꼼꼼하게 따져줘야 했다.

반응형
반응형

< 전체 과정 >

로그인 -> 이메일 -> 비밀번호 -> 확인

 

 

1. 프로그램 실행

npm run dev

-> server와 client 모두 실행, client의 LandingPage가 로드된다.

 

 

2. LandingPage 디자인 수정

 

<div style={{
            display: 'flex', justifyContent: 'center', alignItems: 'center',
            width: '100%', height: '100vh'
}}>
     <h2>시작 페이지</h2>
</div>

 

 

- 페이지 새로고침 방지

event.preventDefault()

 

 

 

 

3. Redux를 이용한 LoginPage

 

import React, {useState} from 'react'
import {useDispatch} from 'react-redux';
import {loginUser} from '../../../_actions/user_action'

function LoginPage(props){
    const dispatch = useDispatch();

    const [Email, setEmail] = useState("")
    const [Password, setPassword] = useState("")
    const onEmailHandler = (event) => {
        setEmail(event.currentTarget.value)
    } 
    const onPasswordHandler = (event) => {
        setPassword(event.currentTarget.value)
    }
    const onSubmitHandler = (event) => {
        event.preventDefault()

        let body = {
            email: Email,
            password: Password
        }

        dispatch(loginUser(body))
        .then(response => {
            if(response.payload.loginSuccess){
                props.history.push('/');
            } else {
                alert('Error ');
            }
        })
    }


    return (
        <div style={{
            display: 'flex', justifyContent: 'center', alignItems: 'center',
            width: '100%', height: '100vh'
        }}>
            <form style={{ display: 'flex', flexDirection: 'column'}}
                onSubmit={onSubmitHandler}
            >
                <label>Email</label>
                <input type="email" value={Email} onChange={onEmailHandler}/>
                <label>Password</label>
                <input type="password" value={Password} onChange={onPasswordHandler}/>

                <br />
                <button>
                    Login
                </button>
            </form>
        </div>
    )
}

export default LoginPage

LoginPage.js

 

 

 

 

4. user_reducer 만들어주기

 

import {
    LOGIN_USER
} from '../_actions/types';

export default function (state = {}, action) {
    switch (action.type) {
        case LOGIN_USER:
            return { ...state, loginSuccess: action.payload }
    
        default:
            return state;
    }
}

user_reducer.js

 

 

-> 로그인 성공 시, loginSuccess가 뜨도록 해준다.

 

 

app.post('/api/users/login',(req, res) => {

  // 요청된 이메일은 데이터베이스에서 있는지 찾는다.
  User.findOne({ email: req.body.email }, (err, user) => {
    console.log(err)
    if(!user){
      return res.json({
        loginSuccess: false,
        message: "제공된 이메일에 해당하는 유저가 없습니다."
      })
    }
    // 요청된 이메일이 데이버테이스에 있다면, 비밀번호가 맞는 비번인지 확인한다.
    user.comparePassword(req.body.password, (err, isMatch) => {
      console.log(err)
      if(!isMatch) 
          return res.json({ loginSuccess: false, message: "비밀번호가 틀렸습니다."})

      // 비밀번호까지 맞다면 토큰을 생성하기
      user.generateToken((err, user) => {
        console.log(err)
        if(err) return res.status(400).send(err);

        // 토큰을 저장한다. 어디에?  쿠키, 로컬스토리지
        res.cookie("x_auth", user.token)  
        .status(200)
        .json({ loginSuccess: true, userId: user._id ,message: "비밀번호가 맞았습니다."})

      })
    })
  })
})

index.js(server)

 

 

-> 전에 index.js에서 설정해준 정보들이 나타난다.

 

 

 

5. reducers 안에 reducer를 관리하는 index.js 를 생성해준다.

 

import { combineReducers } from 'redux';
import user from './user_reducer';

const rootReducer = combineReducers({
    user
})

export default rootReducer;

index.js

 

-> reducer들은 여기에서 가져와서 사용하기로 한다.

 

 

 

 

6. user_action 만들기

 

import axios from 'axios';
import {
    LOGIN_USER
} from './types';
export function loginUser(dataTosubmit){
    const request = axios.post('/api/users/login', dataTosubmit)
        .then(response => response.data)

    return {
        type: LOGIN_USER,
        payload: request
    }
}

user_action.js

 

-> login action을 생성해준다.

 

 

 

 

6. 크롬에서 확인

 

Redux DevTools 라는 크롬 익스텐션을 설치해서

로그인 후 loginSuccess 가 뜨는지 확인한다.

 

반응형

'Programming > MERN' 카테고리의 다른 글

HTML 정리  (0) 2020.12.22
! 인증 체크하기 !  (0) 2020.11.10
Node.js 와 스프링의 차이 🤫  (0) 2020.11.10
🌺 회원가입 페이지 만들기 🌺  (0) 2020.11.09
CSS Framework / Redux 개념 및 설정  (0) 2020.11.02

+ Recent posts