본문 바로가기
개인숙제

react와 python을 사용하여 과제 3 ,4번 해결하기

by useSword 2024. 2. 27.

https://github.com/1489ehdghks/python-react

 

GitHub - 1489ehdghks/python-react

Contribute to 1489ehdghks/python-react development by creating an account on GitHub.

github.com

 

프로젝트 목표

1. flask_sqlalchemy + flask + react를 통해 DB , 서버 , 클라이언트를 전부 관여해보기
2. flask_login와 react에서의 회원가입 및 로그인 상태 구현하기
3. CRUD를 활용하여 과제를 완성해보기

 

 

 

힘들었던 부분 

1. 로그인 상태 구현하고 그 값을 AuthProvider을 통해 로그인 유지와 서버로 데이터 보내기

import React from 'react';
import { BrowserRouter } from 'react-router-dom';
import { AuthProvider } from './Auth/AuthContext';
import AppRoutes from './routes';

function App() {
  return (
    <AuthProvider>
      <BrowserRouter>
        <AppRoutes />
      </BrowserRouter>
    </AuthProvider>

  );
}

export default App;

 

 

컨텍스트(Context)는 React에서 전역 데이터(예: 인증된 사용자 정보, 테마 설정 등)를 관리하고 컴포넌트 트리에 걸쳐 이 데이터를 공유할 수 있는 방법을 제공합니다. 
createContext 함수를 사용하여 컨텍스트를 생성하고, 이 컨텍스트의 Provider를 사용하여 하위 컴포넌트에 데이터를 공급할 수 있습니다

 

 

로그인

//AuthContext.js

 

이곳에서 중요하게 봐야하는 부분은 currentUser이다. 이 정보를 서버에 보내줘야하기 때문이다.

const AuthContext = createContext();

export function useAuth() {
    return useContext(AuthContext);
}

export const AuthProvider = ({ children }) => {
    const [currentUser, setCurrentUser] = useState(null);

    useEffect(() => {
        const storedUserData = localStorage.getItem('user');
        if (storedUserData) {
            const userData = JSON.parse(storedUserData);
            setCurrentUser(userData);
        }
    }, []);

    const login = (userData) => {
        localStorage.setItem('user', JSON.stringify(userData));
        setCurrentUser(userData);
    };

    const logout = () => {
        localStorage.removeItem('user');
        setCurrentUser(null);
    };

    const value = { currentUser, login, logout };

    return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
};

 

//LoginModal.js

여기서 중요한 내용은 작성한 값이 loginInfo에 남고 사용자가 로그인 버튼을 클릭하면 그 정보를 credentials: 'include'을 통해 세션으로 입력된 로그인 정보를 서버의 /login 엔드포인트로 POST 요청을 보냄.

 

 

서버로부터 응답을 받으면, 응답 데이터에서 사용자 정보를 추출하여 글로벌 상태 관리 (AuthContext)에 저장함. 

이 과정에서 로컬 스토리지에도 사용자 정보를 저장하여 페이지 새로고침 후에도 로그인 상태를 유지할 수 있게함.

 

    const [loginInfo, setLoginInfo] = useState({
        userID: '',
        password: '',
    });
    
    const handleSubmit = async (e) => {
        e.preventDefault();
        setError('');

        try {
            const response = await fetch('http://localhost:5000/login', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json'
                },
                credentials: 'include',
                body: JSON.stringify(loginInfo)
            });

            if (!response.ok) {
                const errorData = await response.json();
                setError(errorData.message || 'Login failed. Please check your userID and password.');
                return;
            }

            const data = await response.json();
            login(data);
            console.log('Login successful', data);
            handleClose();
        } catch (error) {
            setError('Login failed. Please check your userID and password.');
        }
    };

 

 

 

/login 엔드포인트에서 클라이언트로부터 받은 로그인 요청을 처리함.

데이터베이스에서 사용자 ID에 해당하는 사용자 정보를 조회함

비밀번호가 일치하면, Flask-Login의 login_user 함수를 호출하여 사용자를 로그인 처리함. 
이 과정에서 사용자의 세션 정보가 생성되며, 클라이언트로 쿠키(세션 ID)를 전송함.
클라이언트로 성공 응답을 보내 로그인 처리를 완료함

# 로그인 관련
@login_manager.user_loader
def load_user(user_id):
    user = Member.query.get(int(user_id))
    print("load_user:", user)
    return user

# 로그인
@app.route('/login', methods=['POST'])
def login():
    data = request.json
    userID = data.get('userID')
    password = data.get('password')

    # 데이터베이스에서 사용자 찾기
    user = Member.query.filter_by(userID=userID).first()

    # 사용자가 존재하고 비밀번호가 일치하는 경우
    if user and check_password_hash(user.userPassword, password):
        login_user(user)
        print("user:", user)
        return jsonify({"message": "Login successful", "user": {"userID": user.userID}}), 200

    else:
        return jsonify({"message": "Invalid userID or password"}), 401

 

 

@login_manager.user_loader이 데코레이터가 적용된 함수는 Flask-Login에 의해 현재 세션에 로그인된 사용자의 ID를 기반으로 사용자 객체를 로드하는 데 사용됩니다.
 사용자가 웹 사이트에 로그인할 때 Flask-Login은 사용자의 ID를 세션에 저장하고, 이후의 요청에서 세션에 저장된 ID를 사용하여 현재 로그인한 사용자를 식별합니다.

 

함수 load_user는 Flask-Login에 의해 호출되며, 매개변수로 사용자의 ID를 받습니다. 이 ID를 사용하여 데이터베이스에서 해당 사용자 객체를 조회합니다

 

 

2. 로그인 상태에서 post를 만들어 currentUser값을 서버로 보내기

 

1번에서 세션을 보낸것과 비슷한 내용이지만 여기서는 서버에서 받아낼 떄 고생을 많이 했다.

포스트 생성할 때 여러가지 일로 인해 고생을 많이했다.
특히 DB에 다른 이름의  데이터를 가지고 있다면 다른 명의 데이터가 들어와도 새로 만들어지는 줄 알고 문제가 없을 줄 알았다. 이 부분을 늦게 깨달아서 백엔드부분을 수정을 많이 했었다.

currentUser 값도 제대로 안받아져서 고생을 했었다.

 

# 포스트생성
@app.route('/api/posts', methods=['POST'])
@cross_origin(supports_credentials=True, origins=['http://localhost:3000'])
# @login_required
def create_post():
    try:
        print("current_user:", current_user.username)
        if not current_user.is_authenticated:
            print("current_user1111111:", current_user)
            return jsonify({"error": "로그인이 필요합니다."}), 401

        try:
            print("2222222")
            data = request.get_json()

            new_post = Post(
                title=data['title'],
                content=data['content'],
                author=current_user.username
            )
            db.session.add(new_post)
            db.session.commit()

            return jsonify({"message": "Post created successfully"}), 201
        except Exception as e:
            return jsonify({"error222222222": str(e)}), 500
    except Exception as e:
        return jsonify({'error1111111111': '11111111'}), 500

 

 

 

배운 내용

1. 메소드가 달라도 사용할 똑같은 URL를 사용할 수 있는가?ㅇㅇ 각각 CRUD로 나누어서 관리가능.

 

2. 인증 토큰 관리?


사용자 인증 및 권한 부여 과정에서 중요한 역할
웹 애플리케이션과 모바일 애플리케이션에서 사용자가 로그인할 때,
서버는 사용자를 인증한 후 고유한 인증 토큰을 생성하여 클라이언트에 반환합니다.
클라이언트는 이후의 요청에서 이 토큰을 서버에 전송함으로써 자신을 인증합니다.
이 과정을 통해 사용자의 로그인 세션을 유지할 수 있으며, 매 요청마다 사용자 이름과 비밀번호를 전송할 필요가 없어짐

 

3. 불안정한 코드들을 주석처리와 stirng 처리하여 점차 문제가 되는 부분을 좁혀내어 확실하게 문제되는 코드를 찾아낼 것

 

4. credentials: 'include'을 통해 클라이언트가 서버로 쿠키를 전송할 수 있음.
올바르게 처리하기 위해  백엔드에서 Access-Control-Allow-Credentials 헤더를 'true'로 설정

'개인숙제' 카테고리의 다른 글

개인숙제 추가수정  (4) 2024.03.01
개인 과제 완료  (0) 2024.02.28
첫번째 개인과제1,2번(파이썬)  (0) 2024.02.22