본문 바로가기
국비학원/수업기록

국비 지원 개발자 과정_Day84

by 루팽 2023. 3. 29.

<시험1-인터페이스 구현평가자체크리스트>

MyBatis 설정파일에서 오류가 발생하는 부분을 해결한 동적 SQL 문을 작성

<settings>
    <setting name="jdbcTypeForNull" value="NULL" />
 </settings>

 

choose, when, otherwise를 이용하여 and 구문이 적절하게 출력될 수 있도록 동적쿼리를 완성

<select id="selectSearchList" resultType="boardResultSet">
	SELECT BOARD_NO, BOARD_TITLE, USER_ID, COUNT, CREATE_DATE
	FROM BOARD B
	JOIN MEMBER ON(BOARD_WRITER=USER_NO)
	<trim prefix="WHERE" prefixOverrides="AND|OR">
		<choose>
			<when test="writer != null">
				AND USER_ID like #{writer}
			</when>
			<when test="title != null">
				AND BOARD_TITLE like #{title}
			</when>
			<when test="content != null">
				AND BOARD_CONTENT like #{content}
			</when>
		</choose>
	</trim>
	<![CDATA[
		AND B.STATUS<> 'N'
		ORDER BY BOARD_NO DESC
	]]>
</select>

 

mybatis-config.xml의 environment  앨리먼트를 완성

<environments default="oracleds">
	<environment id="oracleds">
		<transactionManager type="JDBC" />
		<dataSource type="POOLED">
			<property name="driver" value="oracle.jdbc.driver.OracleDriver" />
			<property name="url"
				value="jdbc:oracle:thin:@192.168.10.33:1521:xe" />
			<property name="username" value="mybatis" />
			<property name="password" value="mybatis" />
		</dataSource>
	</environment>
</environments>

 

mybatis-config.xml 파일 안에 작성될 typeAliases 앨리먼트를 완성

<typeAliases>
	<typeAlias type="com.kh.myBatis.member.model.vo.Member" alias="Member" />
	<typeAlias type="com.kh.myBatis.board.model.vo.Board" alias="Board" />
	<typeAlias type="com.kh.myBatis.board.model.vo.SearchCondition" alias="SearchCondition" />
</typeAliases>

 

 

<시험2-인터페이스 구현서술형(신)>

mapper.xml 에 작성하는 namespace의 역할

Mapper들을 구분하는 식별자.

namespace 속성에는 mapper.xml과 매핑될 mapper 인터페이스 이름을 패키지를 포함하여 작성해 준다.

 

영속성 Framework

데이터의 저장, 조회, 변경, 삭제를 다루는 클래스 및 설정 파일들을 라이브러리화하여 구현한 프레임워크

 

mybatis-config.xml 에서 <environments> 태그에 대해 설명

mybatis에서 연동할 Database 정보를 등록한다.

 

insert, update, delete의 경우 resultType이나 resultMap을 사용하지 않는 이유

resultType이나 resultMap은 조회한 결과를 담을 때 사용하는데,

insert, update, delete는 적용 성공한 로우의 개수에 따라 자동으로 int타입의 값을 반환해 주기에 사용하지 않는다.

 

DBCP 빈을 생성할 때 defaultAutoCommit 속성을 지정하는 차이점

true면 풀에 의해서 생성된 커넥션은 커넥션이 종료되기 전에 자동으로 commit 처리된다.

false로 설정하면 애플리케이션에서 트랜잭션 처리가 되어 있지 않은 경우에는 INSERT 쿼리나 UPDATE 쿼리가 제대로 반영되지 않는다.

 

myBatis 빈 생성 시 configLocation 속성에 들어갈 내용(value)

<property name="configLocation" value="WEB-INF/mybatis-config.xml"/>

 

DEPT 테이블의 특정 row의 dname과 loc를 수정하는 기능을 작성할 때, mapper xml에 알맞은 태그

<update id="updateDept" parameterType="deptDto">
	UPDATE DEPT
	SET dname = #{dname}
		,loc = #{loc}
	WHERE DEPTNO = #{deptno}
</update>

 

config.xml에서 domain.blog.Author 클래스를 Author라는 별칭으로 사용하려고 할 때, 필요한 태그와 해당 태그의 속성에 값을 넣어서 작성

<typeAliases>
	<typeAlias alias="Author" type="domain.blog.Author"/>
</typeAliases>

 


 

 LocalStorage
저장한 데이터를 명시적으로 지우지 않는 이상 영구적으로 보관이 가능

도메인마다 별도로 로컬 스토로지가 생성됨

브라우저를 종료해도 데이터는 보관되어 다음번 접속에도 그 데이터를 사용할 수 있음


SessionStorage
SessionStorage는 데이터의 지속성과 액세스 범위에 특수한 제한이 존재

브라우저가 종료되면 데이터도 같이 지워짐

 

<리액트 로그인 진행중 - App.jsx>

import { Route, Routes, useNavigate } 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 Toast from './components/Toast';
import { useDispatch, useSelector } from 'react-redux';
import { useEffect } from 'react';
import { setToastMsg } from './redux/toastStatus/action';
import SignupPage from './components/auth/SignupPage';
import KhLoginPage from './components/auth/KhLoginPage';
import { onAuthChange } from './service/authLogic';
import { memberListDB } from './service/dbLogic';

function App({authLogic, imageUploader}) {
  const navigate = useNavigate()
  const dispatch = useDispatch()
  const ssg = sessionStorage;
  const toastStatus = useSelector(state => state.toastStatus)
  useEffect(() => {
    const async = async() => {
      const auth = authLogic.getUserAUth()
      // 현재 인증된 사용자 정보를 가져온다
      const user = await onAuthChange(auth)
      // 사용자가 있을 경우(userId가 존재) - 구글 로그인으로 사용자 정보를 가지고 있을 때
      // user정보가 있으면 sessionStorage에 담는다 - email
      if(user) {
        console.log('user정보가 있을 때')
        ssg.setItem('email', user.email)
        const res = await memberListDB({MEM_ID: user.uid, type: 'auth'})
        //오라클 서버의 회원집합에 uid가 존재할 경우 - 세션스토리지에 값을 담는다
        if(res.data) {
          const temp = JSON.stringify(res.data)
          const jsonDoc = JSON.parse(temp)
          ssg.setItem('nickname', jsonDoc[0].MEM_NICKNAME)
          ssg.setItem('status', jsonDoc[0].MEM_STATUS)
          ssg.setItem('auth', jsonDoc[0].MEM_AUTH)
          ssg.setItem('no', jsonDoc[0].MEM_NO)
          navigate("/")
          return // 렌더링이 종료됨
        }
        // 구글 로그인을 했지만 false일 경우
        //if() {
        //}
        //오라클 서버의 회원집합에 uid가 존재하지 않을 경우 
        else {

        }
      }
      // 사용자 정보가 없을 경우
      else {
        console.log('user정보가 없을 때')
        if(ssg.getItem('email')){
          // 세션 스토리지에 있는 값 모두 삭제하기
          ssg.clear()
          window.location.reload()
        } // end of inner if
      } // end of else
    }
  }, [dispatch])

  return (
    <>
      <div style={{height: '100vh'}}>
        {toastStatus.status && <Toast />}
        <Routes>
          <Route path='/login' exact={true} element={<KhLoginPage authLogic={authLogic} />} />
          <Route path='/' exact={true} element={<HomePage />} />
          <Route path='/home' exact={true} element={<HomePage />} />
          <Route path='/auth/signup' exact={true} element={<SignupPage authLogic={authLogic} />} />
          <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>
      </div>
    </>
  );
}

export default App;

 

<리액트 로그인 진행중 - KhLoginPage.jsx>

import React, { useEffect, useState } from 'react';
import { Link, useNavigate } from 'react-router-dom';
import { loginEmail, loginGoogle } from '../../service/authLogic';
import { DividerDiv, DividerHr, DividerSpan, GoogleButton, LoginForm, MyH1, MyInput, MyLabel, MyP, PwEye, SubmitButton } from '../styles/FormStyle';

const KhLoginPage = ({authLogic}) => {
  console.log('LoginPage'); // a태그 사용하지 않기 -> Link(react-router-dom), useNavigate
  const navigate = useNavigate()
  const auth = authLogic.getUserAuth()

  const[submitBtn, setSubmitBtn] = useState({
    disabled: true,
    bgColor: 'rgb(175, 210, 244)',
    hover: false
  });

  const [tempUser, setTempUser] = useState({
    email: '',
    password: ''
  });

  const [passwordType, setPasswordType] = useState({
      type:'password',
      visible:false
  });

  useEffect(()=> {
    if(tempUser.email!==""&&tempUser.password!==""){ 
      setSubmitBtn({disabled:false, bgColor: 'rgb(105, 175, 245)'});
    } else {
      setSubmitBtn({disabled:true, bgColor: 'rgb(175, 210, 244)'});
    }
  },[tempUser]);

  const changeUser = (e) => {
    const id = e.currentTarget.id;
    const value = e.target.value;
    setTempUser({...tempUser, [id]: value});
  };

  const passwordView = (e) => {
    const id = e.currentTarget.id;
    if(id==="password") {
      if(!passwordType.visible) {
        setPasswordType({...passwordType, type: 'text', visible: true});
      } else {
        setPasswordType({...passwordType, type: 'password', visible: false});
      }
    }
  };

  const toggleHover = () => {
    if(submitBtn.hover){
      setSubmitBtn({...submitBtn, hover: false, bgColor: 'rgb(105, 175, 245)'});
    } else {
      setSubmitBtn({...submitBtn, hover: true, bgColor: 'rgb(58, 129, 200)'});
    }
  }

  const loginE = async() => {
    // 이메일 로그인 구현
    console.log(tempUser)
    try {
      const result = await loginEmail(auth, tempUser)
      console.log(result)
      console.log(result.user.uid)
      window.sessionStorage.setItem('userId', result.user.uid)
      window.localStorage.setItem('userId', result.user.uid)
      window.localStorage.setItem('member', JSON.stringify({'mem_id': 'test', 'mem_pw': '123'}))
      // 현재 내가 바라보는 URL /login
      navigate("/") // Route path="/" HomePage
    } catch (error) {
      console.log(error + ": 로그인 에러")
    }
  }

  const loginG = async() => {
    // 구글 로그인 구현
    try {
      const result = await loginGoogle(authLogic.getUserAuth(), authLogic.getGoogleAuthProvider())
      console.log(result.data)
      //navigate("/")
      //window.location.reload()
    } catch (error) {
      console.log(error + ': 구글 로그인 에러')
    }
  }
  return (
    <>
      <LoginForm>
        <MyH1>로그인</MyH1>
        <MyLabel htmlFor="email"> 이메일
          <MyInput type="email" id="email" name="mem_email" placeholder="이메일를 입력해주세요." 
            onChange={(e)=>changeUser(e)} />
        </MyLabel>
        <MyLabel htmlFor="password"> 비밀번호
          <MyInput type={passwordType.type} autoComplete="off" id="password" name="mem_password" placeholder="비밀번호를 입력해주세요."
            onChange={(e)=>changeUser(e)}/>
          <div id="password" onClick={(e)=> {passwordView(e)}} style={{color: `${passwordType.visible?"gray":"lightgray"}`}}>
            <PwEye className="fa fa-eye fa-lg"></PwEye>
          </div>
        </MyLabel>
        <SubmitButton type="button"  disabled={submitBtn.disabled} style={{backgroundColor:submitBtn.bgColor}}  
          onMouseEnter={toggleHover} onMouseLeave={toggleHover} onClick={()=>{loginE()}}>
          로그인
        </SubmitButton>
        <DividerDiv>
          <DividerHr />
          <DividerSpan>또는</DividerSpan>
        </DividerDiv>
        <GoogleButton type="button" onClick={()=>{loginG();}}>
          <i className= "fab fa-google-plus-g" style={{color: "red", fontSize: "18px"}}></i>&nbsp;&nbsp;Google 로그인
        </GoogleButton>
        <MyP style={{marginTop:"30px"}}>신규 사용자이신가요?&nbsp;<Link to="/auth/signup" className="text-decoration-none" style={{color: "blue"}}>계정 만들기</Link></MyP>
        <MyP>이메일를 잊으셨나요?&nbsp;<Link to="/login/findEmail" className="text-decoration-none" style={{color: "blue"}}>이메일 찾기</Link></MyP>
        <MyP>비밀번호를 잊으셨나요?&nbsp;<Link to="/login/resetPwd" className="text-decoration-none" style={{color: "blue"}}>비밀번호 변경</Link></MyP>
      </LoginForm>
    </>
  );
}

export default KhLoginPage;

 

<리액트 로그인 진행중 - HomaPage.jsx>

import React from 'react'
import { Button } from 'react-bootstrap'
import { useNavigate } from 'react-router-dom'
import BlogHeader from '../include/BlogHeader'
import KakaoMap from '../kakao/KakaoMap'
import {ContainerDiv, FormDiv, HeaderDiv} from '../styles/FormStyle'

const HomePage = () => {
  const member = window.localStorage.getItem('member')
  console.log(JSON.parse(member))
  const jsonDoc = JSON.parse(member)
  console.log(jsonDoc.mem_id + ', ' + jsonDoc.mem_pw)
  const navigate = useNavigate()
  const handleLogin = () => {
    console.log('로그인 요청');
    navigate('/login')
  }

  return (
    <>
      <ContainerDiv>
        <BlogHeader />
        <HeaderDiv>
          <h1 style={{marginLeft:"10px"}}>터짐블로그</h1>
          <Button onClick={handleLogin}>로그인</Button>
        </HeaderDiv>
        <FormDiv>
          <div>이벤트존</div>
          <hr style={{height:"2px"}} />
          <div>추천 수업존</div>
          <hr style={{height:"2px"}} />
          <div><KakaoMap /></div>
          <div>카카오맵존</div>
          <hr style={{height:"2px"}} />
        </FormDiv>
      </ContainerDiv>
    </>
  )
}

export default HomePage

 

<리액트 로그인 진행중 - authLogic.js>

import { createUserWithEmailAndPassword, EmailAuthProvider, getAuth, GoogleAuthProvider, sendEmailVerification, signInWithEmailAndPassword, signInWithPopup } from 'firebase/auth'

class AuthLogic {
  constructor() {
    this.auth = getAuth()
    this.googleProvider = new GoogleAuthProvider()
  }
  getUserAuth = () => {
    return this.auth
  }
  getGoogleAuthProvider = () => {
    return this.googleProvider
  }
}
export default AuthLogic

export const onAuthChange = (auth) => {
  return new Promise((resolve) => {
    // 사용자가 바뀌었을 때 콜백함수를 받아서
    auth.onAuthStateChanged((user) => {
    resolve(user)
    });
  }) // end of Promise
} // end of onAuthChange

// 로그아웃 버튼 클릭시 호출하기
export const logout = (auth) => {
  return new Promise((resolve, reject) => {
    auth.signOut().catch(e => reject(e + '로그아웃 오류입니다.'))
    // 로그인 성공시 세션 스토리지에 담아둔 정보를 모두 지운다
    sessionStorage.clear();
    // 서비스를 더 이상 사용하지 않는 경우이므로 돌려줄 값은 없다
    resolve();
  })
} // end of logout

export const loginEmail = (auth, user) => {
  console.log(auth)
  console.log(user.email + user.password)
  return new Promise((resolve, reject) => {
    signInWithEmailAndPassword(auth, user.email, user.password)
    .then((userCredential) => {
      // Signed in
      const user = userCredential.user;
      console.log(user)
      resolve(userCredential)
    })
    .catch((error) => {
      const errorCode = error.code;
      const errorMessage = error.message;
      console.log(errorCode + ", " + errorMessage)
      reject(error)
    });
  })
}

 // 로그인 시도시 구글인증인지 아니면 깃허브 인증인지 문자열로 넘겨받음
  // 구글 인증인 경우 - Google
  // 깃허브 인증인 경우 - Github
  export const loginGoogle = (auth, googleProvider) => {
    return new Promise((resolve, reject) => {
      signInWithPopup(auth, googleProvider).then(
        (result) => {
          const user = result.user; // 구글에 등록되어있는 profile정보가 담겨있음
          console.log(user);
          resolve(user)
        }
      ).catch(e => reject(e))
    })
  }

  export const signupEmail = (auth, user) => {
    return new Promise((resolve, reject) => {
      createUserWithEmailAndPassword(auth, user.email, user.password)
        .then((userCredential) => {
          sendEmail(userCredential.user).then(() => {
            resolve(userCredential.user.uid);
          });
        })
        .catch((e) => reject(e));
    });
  };

  export const linkEmail = (auth, user) => {
    console.log(auth);
    console.log(auth.currentUser);
    console.log(user);
    return new Promise((resolve, reject) => {
      console.log(user.email + "," + user.password);
      const credential = EmailAuthProvider.credential(user.email, user.password);
      console.log(credential);
      console.log(auth.currentUser.uid);
      resolve(auth.currentUser.uid)
      /* 인증정보가 다른 사용자 계정에 이미 연결되어 있다면 아래 코드 에러 발생함
      linkWithCredential(auth.currentUser, credential)
        .then((usercred) => {
          console.log(usercred);
          const user = usercred.user;
          console.log("Account linking success", user.uid);
          resolve(user.uid);
        })
        .catch((e) => reject(e));
      */
    });
  };

  export const sendEmail = (user) => {
    return new Promise((resolve, reject) => {
      sendEmailVerification(user)
        .then(() => {
          resolve("해당 이메일에서 인증메세지를 확인 후 다시 로그인 해주세요.");
        })
        .catch((e) => reject(e + ": 인증메일 오류입니다."));
    });
  };

댓글