회원제로 운영되는 서비스 - 여러 게시판 유형
(ex. QnA인데 양도, 매매 등으로 검색조건이 꼭 필요한 게시판, 리뷰게시판, 예매게시판 등)
게시판의 유형에 따라 첨부파일이 필요한 경우도 있고 필요 없는 경우도 있음
게시판 유형에따라 댓글 처리 테이블을 별도로 설계함
리뷰게시판 - 리뷰댓글테이블(ex. review_comment)
QnA게시판 - QnA댓글테이블(ex. qna_comment)
예매게시판 - 예매댓글테이블(ex. reserve_comment)
첨부파일은 통합 테이블로 관리하기로 함 - mblog_file
MEM_NO NUMBER(5) NOT NULL, 시퀀스--seq_member_no.nextval 자동채번-사용자가 입력하는 값이 아님
1)MEM_UID VARCHAR2(20 BYTE), --사용자가 입력한 값
2)MEM_PW VARCHAR2(10 BYTE) NOT NULL, --사용자가 입력한 값
MEM_PW
3)MEM_NAME VARCHAR2(30 BYTE) NOT NULL, --사용자가 입력한 값
4)MEM_NICKNAME VARCHAR2(30 BYTE), --사용자가 입력한 값
5)MEM_EMAIL VARCHAR2(30 BYTE), --사용자가 입력한 값
6)MEM_TEL VARCHAR2(20 BYTE), --사용자가 입력한 값
7)MEM_GENDER VARCHAR2(1 BYTE), --사용자가 입력한 값
8)MEM_BIRTHDAY VARCHAR2(20 BYTE), --사용자가 입력한 값
9)MEM_ZIPCODE VARCHAR2(6 BYTE), --사용자가 입력한 값
10)MEM_ADDR VARCHAR2(100 BYTE), --사용자가 입력한 값
11_MEM_ADDR_DTL VARCHAR2(50 BYTE), --사용자가 입력한 값
MEM_STATUS VARCHAR2(20 BYTE), --디폴트0, 일반1, 블랙리스트-입력받는게 아님
MEM_AUTH VARCHAR2(20 BYTE) --인증(관리자, 일반회원, 강사)-입력받는게 아님
<리액트 스프링 회원가입 - App.jsx>
import { Route, Routes } from 'react-router-dom';
import LoginPage from './components/auth/LoginPage';
import KakaoRedirectHandler from './components/kakao/KakaoRedirectHandler';
import Profile from './components/kakao/Profile';
import MemberPage from './components/page/MemberPage';
import HomePage from './components/page/HomePage';
import DeptPage from './components/page/DeptPage';
import DeptDetail from './components/dept/DeptDetail';
import RepleBoardPage from './components/page/RepleBoardPage';
import Signup from './components/member/Signup';
function App({imageUploader}) {
return (
<>
<Routes>
<Route path='/' exact={true} element={<LoginPage />} />
<Route path='/home' exact={true} element={<HomePage />} />
<Route path='/member/signup' exact={true} element={<Signup />} />
<Route path='/repleboard' element={<RepleBoardPage />} />
<Route path='/dept/:gubun' element={<DeptPage imageUploader={imageUploader} />} />
{/* 컴포넌트 함수를 호출하는 것이다 - 마운트(화면이 보여지는 것) - return이 호출되었다 */}
<Route path='/deptdetail/:deptno' element={<DeptDetail imageUploader={imageUploader} />} />
<Route path='/auth/kakao/callback' exact={true} element={<KakaoRedirectHandler />} />
<Route path="/member" exact={true} element={<MemberPage imageUploader={imageUploader} />} />
<Route path="/profile" exact={true} element={<Profile />} />
</Routes>
</>
);
}
export default App;
<리액트 스프링 회원가입 - LoginPage.jsx>
import React from 'react'
import Image from 'react-bootstrap/Image'
import { Link } from 'react-router-dom'
const LoginPage = () => {
const REDIRECT_URI = "http://localhost:3000/auth/kakao/callback"
const KAKAO_AUTH_URL = `https://kauth.kakao.com/oauth/authorize?client_id=${process.env.REACT_APP_KAKAO_API_KEY}&redirect_uri=${REDIRECT_URI}&response_type=code`
return (
<>
<div>
<a href={KAKAO_AUTH_URL}>
<Image src='/images/kakao/kakao_login_medium_wide.png' />
</a>
</div>
<Link to={"/member/signup/"} className="btn btn-primary">회원가입</Link>
</>
)
}
export default LoginPage
<리액트 스프링 회원가입 - Signup.jsx>
import React, { useCallback, useState } from 'react'
import { useNavigate } from 'react-router-dom'
import { memberInsertDB } from '../../service/dbLogic'
import { BButton, ContainerDiv, FormDiv, HeaderDiv } from '../styles/FormStyle'
// 회원가입 페이지
const Signup = () => { // 컴포넌트 함수
// useXXX -> 리액트 훅(Hook), 16.8버전 등장
// -> 그 전까지는 클래스 지원 되던것을 함수형 프로그래밍에대한 이점으로 훅 지원하게됨
const navigate = useNavigate()
const[mem_uid, setMemuid] = useState("")
const[mem_pw, setMempw] = useState("")
const[mem_name, setMemname] = useState("")
const[mem_nickname, setMemnickname] = useState("")
const[mem_email, setMememail] = useState("")
const[mem_tel, setMemtel] = useState("")
const[mem_gender, setMemgender] = useState("")
const[mem_birthday, setMembirthday] = useState("")
const handleID= useCallback((e) => {
setMemuid(e)
console.log(e)
}, [])
const handlePW= useCallback((e) => {
setMempw(e)
}, [])
const handleName= useCallback((e) => {
setMemname(e)
}, [])
const handleNickname= useCallback((e) => {
setMemnickname(e)
}, [])
const handleEmail= useCallback((e) => {
setMememail(e)
}, [])
const handleTel= useCallback((e) => {
setMemtel(e)
}, [])
const handleGender= useCallback((e) => {
setMemgender(e)
}, [])
const handleBirthday= useCallback((e) => {
setMembirthday(e)
}, [])
// Post, @RequestBody, {} -> Map or VO -> 비동기처리 -> Promise(resolve, reject)
// async - await
const memberInsert = async() => {
const member = {
mem_uid: mem_uid,
mem_pw: mem_pw,
mem_name: mem_name,
mem_nickname: mem_nickname,
mem_email: mem_email,
mem_tel: mem_tel,
mem_gender: mem_gender,
mem_birthday: mem_birthday
}
const res = await memberInsertDB(member)
console.log(res + ", " + res.data)
if(!res.data) {
console.log('회원가입에 실패하였습니다.')
} else {
console.log('회원가입 성공!')
// 회원가입 성공시 로그인 화면을 이동
navigate("/")
}
}
return (
<>
<ContainerDiv>
<HeaderDiv>
<h3 style={{marginLeft:"10px"}}>회원가입</h3>
</HeaderDiv>
<FormDiv>
<div style={{width:"100%", maxWidth:"2000px"}}>
<div style={{display: 'flex', justifyContent: 'space-between', marginBottom:'5px'}}>
<h4>아이디</h4>
</div>
<input id="mem_uid" type="text" maxLength="50" placeholder="아이디를 입력하세요."
style={{width:"200px",height:'40px' , border:'1px solid lightGray', marginBottom:'5px'}} onChange={(e)=>{handleID(e.target.value)}} />
<div style={{display: 'flex', justifyContent: 'space-between', marginBottom:'5px'}}>
<h4>비밀번호</h4>
</div>
<input id="mem_pw" type="text" maxLength="50" placeholder="비밀번호를 입력하세요."
style={{width:"200px",height:'40px' , border:'1px solid lightGray', marginBottom:'5px'}} onChange={(e)=>{handlePW(e.target.value)}} />
<div style={{display: 'flex', justifyContent: 'space-between', marginBottom:'5px'}}>
<h4>비밀번호 확인</h4>
</div>
<input id="mem_pw2" type="text" maxLength="50" placeholder="비밀번호를 확인하세요."
style={{width:"200px",height:'40px' , border:'1px solid lightGray', marginBottom:'5px'}} />
<div style={{display: 'flex', justifyContent: 'space-between', marginBottom:'5px'}}>
<h4>이름</h4>
</div>
<input id="mem_name" type="text" maxLength="50" placeholder="이름을 입력하세요."
style={{width:"200px",height:'40px' , border:'1px solid lightGray', marginBottom:'5px'}} onChange={(e)=>{handleName(e.target.value)}} />
<div style={{display: 'flex', justifyContent: 'space-between', marginBottom:'5px'}}>
<h4>닉네임</h4>
</div>
<input id="mem_nickname" type="text" maxLength="50" placeholder="닉네임을 입력하세요."
style={{width:"200px",height:'40px' , border:'1px solid lightGray', marginBottom:'5px'}} onChange={(e)=>{handleNickname(e.target.value)}} />
<div style={{display: 'flex', justifyContent: 'space-between', marginBottom:'5px'}}>
<h4>이메일</h4>
</div>
<input id="mem_email" type="text" maxLength="50" placeholder="이메일을 입력하세요."
style={{width:"200px",height:'40px' , border:'1px solid lightGray', marginBottom:'5px'}} onChange={(e)=>{handleEmail(e.target.value)}} />
<div style={{display: 'flex', justifyContent: 'space-between', marginBottom:'5px'}}>
<h4>전화번호</h4>
</div>
<input id="mem_tel" type="text" maxLength="50" placeholder="전화번호를 입력하세요."
style={{width:"200px",height:'40px' , border:'1px solid lightGray', marginBottom:'5px'}} onChange={(e)=>{handleTel(e.target.value)}} />
<div style={{display: 'flex', justifyContent: 'space-between', marginBottom:'5px'}}>
<h4>성별</h4>
</div>
<input id="mem_gender" type="text" maxLength="50" placeholder="성별을 선택하세요."
style={{width:"200px",height:'40px' , border:'1px solid lightGray', marginBottom:'5px'}} onChange={(e)=>{handleGender(e.target.value)}} />
<div style={{display: 'flex', justifyContent: 'space-between', marginBottom:'5px'}}>
<h4>생일</h4>
</div>
<input id="mem_birthday" type="text" maxLength="50" placeholder="생일을 입력하세요."
style={{width:"200px",height:'40px' , border:'1px solid lightGray', marginBottom:'5px'}} onChange={(e)=>{handleBirthday(e.target.value)}} />
<div style={{display: 'flex', justifyContent: 'space-between', marginBottom:'5px'}}>
<BButton onClick={memberInsert}>가입</BButton>
<hr style={{margin:'10px 0px 10px 0px'}}/>
</div>
</div>
</FormDiv>
</ContainerDiv>
</>
)
}
export default Signup
<리액트 스프링 회원가입 - dbLogic.js>
import axios from "axios";
export const memberListDB = (member) => {
return new Promise((resolve, reject) => {
try {
const response = axios({
method: "get",
url: process.env.REACT_APP_SPRING_IP + "member/memberList",
params: member,
});
resolve(response);
} catch (error) {
reject(error);
}
});
};
export const memberInsertDB = (member) => {
return new Promise((resolve, reject) => {
console.log(member)
try {
const response = axios({
method: "post", // @RequestBody
url: process.env.REACT_APP_SPRING_IP + "member/memberInsert",
data: member, // post방식 data
});
resolve(response);
} catch (error) {
reject(error);
}
});
};
export const memberUpdateDB = (member) => {
return new Promise((resolve, reject) => {
console.log(member)
try {
const response = axios({
method: "post", // @RequestBody
url: process.env.REACT_APP_SPRING_IP + "member/memberUpdate",
data: member, // post방식 data
});
resolve(response); // 요청 처리 성공했을 때
} catch (error) {
reject(error); // 요청 처리 실패했을 때
}
});
};
export const memberDeleteDB = (member) => {
return new Promise((resolve, reject) => {
console.log(member)
try {
const response = axios({
method: "get",
url: process.env.REACT_APP_SPRING_IP + "member/memberDelete",
params: member,
});
resolve(response); // 요청 처리 성공했을 때
} catch (error) {
reject(error); // 요청 처리 실패했을 때
}
});
};
export const deptInsertDB = (dept) => {
return new Promise((resolve, reject) => {
try {
const response = axios({
method: "post", // @RequestBody
url: process.env.REACT_APP_SPRING_IP + "dept/deptInsert",
data: dept, // post방식 data
});
resolve(response);
} catch (error) {
reject(error);
}
});
};
export const deptUpdateDB = (dept) => {
return new Promise((resolve, reject) => {
console.log(dept)
try {
const response = axios({
method: "post", // @RequestBody
url: process.env.REACT_APP_SPRING_IP + "dept/deptUpdate",
data: dept, // post방식 data
});
resolve(response); // 요청 처리 성공했을 때
} catch (error) {
reject(error); // 요청 처리 실패했을 때
}
});
};
export const deptDeleteDB = (dept) => {
return new Promise((resolve, reject) => {
try {
const response = axios({
method: "get",
url: process.env.REACT_APP_SPRING_IP + "dept/deptDelete",
params: dept,
});
resolve(response); // 요청 처리 성공했을 때
} catch (error) {
reject(error); // 요청 처리 실패했을 때
}
});
};
export const deptListDB = (dept) => {
return new Promise((resolve, reject) => {
try {
const response = axios({
method: "get",
url: process.env.REACT_APP_SPRING_IP + "dept/deptList",
params: dept, // 쿼리스트링은 header에 담김 - get방식 params
});
resolve(response);
} catch (error) {
reject(error);
}
});
};
<리액트 스프링 회원가입 - RestMemberController.java>
package com.example.demo.controller;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
// import org.slf4j.Logger;
// import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.example.demo.logic.BoardLogic;
import com.example.demo.logic.MemberLogic;
import com.google.gson.Gson;
@RestController
@RequestMapping("/member/*")
public class RestMemberController {
// Logger logger = LoggerFactory.getLogger(RestMemberController.class);
Logger logger = LogManager.getLogger(RestMemberController.class);
@Autowired
private MemberLogic memberLogic = null;
/**
* 회원 목록
* @param pMap
* @return
*/
@GetMapping("memberList")
public String memberList(@RequestParam Map<String, Object> pMap) {
logger.info("memberList 호출");
String temp = null;
List<Map<String, Object>> mList = new ArrayList<>();
mList = memberLogic.memberList(pMap);
Gson g = new Gson();
temp = g.toJson(mList);
return temp;
}
/**
* 회원 등록
* @param pMap
* @return
*/
@PostMapping("memberInsert")
public String memberInsert(@RequestBody Map<String, Object> pMap) {
// 리액트에서 body에 {}객체리터럴로 넘겨준 정보를 Map이나 VO에 담을 수 있다
logger.info("memberInsert 호출");
logger.info(pMap);
int result = 0;
result = memberLogic.memberInsert(pMap);
return String.valueOf(result);
}
/**
* 회원 정보 수정
* @param pMap
* @return
*/
@PostMapping("memberUpdate")
public String memberUpdate(@RequestBody Map<String, Object> pMap) {
logger.info("memberUpdate 호출");
logger.info(pMap);
int result = 0;
result = memberLogic.memberUpdate(pMap);
return String.valueOf(result);
}
/**
* 회원 정보 삭제
* @param pMap
* @return
*/
@GetMapping("memberDelete")
public String memberDelete(@RequestParam Map<String, Object> pMap) {
logger.info("memberDelete 호출");
logger.info(pMap);
int result = 0;
result = memberLogic.memberDelete(pMap);
return String.valueOf(result);
}
}
<리액트 스프링 회원가입 - MemberLogic.java>
package com.example.demo.logic;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.example.demo.dao.MemberDao;
/*
* 모델계층(MemberLogic[직접 오라클과 연동하지 않음] + MemberDao[데이터셋])
* 공통된 관심사 분리(AspectJ 프레임워크 - 오픈소스 참고)
* 트랜잭션 처리 지원받음
*/
@Service
public class MemberLogic {
Logger logger = LogManager.getLogger(MemberLogic.class);
@Autowired
private MemberDao memberDao = null;
public List<Map<String, Object>> memberList(Map<String, Object> pMap) {
logger.info("memberList 호출");
List<Map<String,Object>> mList = new ArrayList<>();
mList= memberDao.memberList(pMap);
return mList;
}
public int memberInsert(Map<String, Object> pMap) {
logger.info("memberInsert 호출");
logger.info(pMap);
int result = 0;
result = memberDao.memberInsert(pMap);
return result;
}
public int memberUpdate(Map<String, Object> pMap) {
logger.info("memberUpdate 호출");
logger.info(pMap);
int result = 0;
result = memberDao.memberUpdate(pMap);
return result;
}
public int memberDelete(Map<String, Object> pMap) {
logger.info("memberDelete 호출");
logger.info(pMap);
int result = 0;
result = memberDao.memberDelete(pMap);
return result;
}
}
<리액트 스프링 회원가입 - MemberDao.java>
package com.example.demo.dao;
import java.util.List;
import java.util.Map;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.beans.factory.annotation.Autowired;
// import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;
// @Repository -> MVC패턴 공부를위해 Service를 사용함
@Service
public class MemberDao {
Logger logger = LogManager.getLogger(MemberDao.class);
@Autowired
private SqlSessionTemplate sqlSessionTemplate = null;
public List<Map<String, Object>> memberList(Map<String, Object> pMap) {
logger.info("memberList 호출");
List<Map<String,Object>> mList = sqlSessionTemplate.selectList("memberList", pMap);
return mList;
}
public int memberInsert(Map<String, Object> pMap) {
logger.info("memberInsert 호출");
logger.info(pMap);
int result = 0;
result = sqlSessionTemplate.update("memberInsert", pMap);
return result;
}
public int memberUpdate(Map<String, Object> pMap) {
logger.info("memberUpdate 호출");
logger.info(pMap);
int result = 0;
result = sqlSessionTemplate.update("memberUpdate", pMap);
return result;
}
public int memberDelete(Map<String, Object> pMap) {
logger.info("memberDelete 호출");
logger.info(pMap);
int result = 0;
result = sqlSessionTemplate.delete("memberDelete", pMap);
return result;
}
}
<리액트 스프링 회원가입 - member.xml>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo">
<select id="getToday" resultType="string" parameterType="string">
SELECT
to_char(sysdate, 'YYYY-MM-DD') FROM dual
</select>
<select id="login" parameterType="map" resultType="string">
select mem_name from member230324
<where>
<if test='mem_uid!=null and mem_uid.length()>0'>
AND mem_uid = #{mem_uid}
</if>
<if test='mem_pw!=null and mem_pw.length()>0'>
AND mem_pw = #{mem_pw}
</if>
</where>
</select>
<!-- 회원 목록 -->
<select id="memberList" parameterType="map" resultType="map">
select mem_uid, mem_name from member230324
<where>
<if test='mem_uid!=null and mem_uid.length()>0'>
AND mem_uid = #{mem_uid}
</if>
</where>
</select>
<!-- 회원 정보 입력 -->
<insert id="memberInsert" parameterType="map">
INSERT INTO member230324(mem_no
<if test="mem_uid != null">
,mem_uid
</if>
<if test="mem_pw != null">
,mem_pw
</if>
<if test="mem_name != null">
,mem_name
</if>
<if test="mem_nickname != null">
,mem_nickname
</if>
<if test="mem_email != null">
,mem_email
</if>
<if test="mem_tel != null">
,mem_tel
</if>
<if test="mem_gender != null">
,mem_gender
</if>
<if test="mem_birthday != null">
,mem_birthday
</if>
)
VALUES (seq_member_no.nextval
<if test="mem_uid != null">
,#{mem_uid}
</if>
<if test="mem_pw != null">
,#{mem_pw}
</if>
<if test="mem_name != null">
,#{mem_name}
</if>
<if test="mem_nickname != null">
,#{mem_nickname}
</if>
<if test="mem_email != null">
,#{mem_email}
</if>
<if test="mem_tel != null">
,#{mem_tel}
</if>
<if test="mem_gender != null">
,#{mem_gender}
</if>
<if test="mem_birthday != null">
,#{mem_birthday}
</if>
)
</insert>
</mapper>
'국비학원 > 수업기록' 카테고리의 다른 글
국비 지원 개발자 과정_Day83 (1) | 2023.03.28 |
---|---|
국비 지원 개발자 과정_Day82 (1) | 2023.03.27 |
국비 지원 개발자 과정_Day80 (0) | 2023.03.23 |
국비 지원 개발자 과정_Day79 (0) | 2023.03.22 |
국비 지원 개발자 과정_Day78 (0) | 2023.03.21 |
댓글