https://github.com/1489ehdghks/python-react
프로젝트 목표
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 |