반응형
SMALL

egovFrame 4.2 에서 기본 템플릿인 simple homepage에 기존 샘플을 붙이기로 했다.

여기서 제공하는 구조를 갖다 쓰기 위함....

 

기존에 쓰던 샘플도 HSQL 을 사용해서 DB연결을 했고 샘플 전용 SQL 파일을 넣어 테이블을 생성해서 활용했다.

이번에도 해당 테이블을 활용해보도록 하자.


1. 구조파악

 

resources 아래 프로젝트 구조

1️⃣ shtdb.sql (DB 스크립트 파일)

HSQLDB를 사용할 때, 초기 테이블 생성 및 샘플 데이터 삽입을 위한 SQL 스크립트 파일.

HSQLDB는 Java 기반의 오픈 소스 관계형 데이터베이스(RDBMS)로, 로컬에서 개발할 때 자바 내장 DB를 사용해서 간단한 SQL문 실행과 테스트를 진행할 수 있다.

특징으로는 서버를 껐다가 켜면 데이터가 초기화된다.

shtdb.sql 같은 SQL 파일이 HSQLDB에서 데이터를 초기화하기 위해 사용되는 것.

 

따라서 내부에 보면 

 

  • CREATE TABLE ... (테이블 생성)
  • INSERT INTO ... (초기 데이터 삽입)
  • ALTER TABLE ... (제약 조건 추가)

이런 내용들이 들어가 있음.

 

 

 

2️⃣ globals.properties (전역 설정 파일 - DB 연결 등)

eGovFrame에서 사용하는 전역 설정값을 저장하는 파일이다.

DB 연결 정보, 파일 경로, 공통 변수 등을 정의한다.


  
#-----------------------------------------------------------------------
#
# globals.properties : 시스템
#
#-----------------------------------------------------------------------
# 1. key = value 구조입니다.
# 2. key값은 공백문자를 포함불가, value값은 공백문자를 가능
# 3. key값으로 한글을 사용불가, value값은 한글사용이 가능
# 4. 줄을 바꿀 필요가 있으면 '\'를 라인의 끝에 추가(만약 '\'문자를 사용해야 하는 경우는 '\\'를 사용)
# 5. Windows에서의 디렉토리 표시 : '\\' or '/' ('\' 사용하면 안됨)
# 6. Unix에서의 디렉토리 표시 : '/'
# 7. 주석문 처리는 #사용
# 8. value값 뒤에 스페이스가 존재하는 경우 서블릿에서 참조할때는 에러발생할 수 있으므로 trim()하거나 마지막 공백없이 properties 값을 설정할것
#-----------------------------------------------------------------------
# 운영서버 타입(WINDOWS, UNIX)
Globals.OsType = WINDOWS
# G4C 연결용 IP (localhost)
Globals.LocalIp = 127.0.0.1
# DB서버 타입(mysql,oracle,altibase,tibero) - datasource 및 sqlMap 파일 지정에 사용됨
Globals.DbType = hsql
Globals.UserName=sa
Globals.Password=
# mysql
#Globals.DriverClassName=net.sf.log4jdbc.DriverSpy
#Globals.Url=jdbc:log4jdbc:mysql://127.0.0.1:3306/sht
#oracle
#Globals.DriverClassName=oracle.jdbc.driver.OracleDriver
#Globals.Url=jdbc:oracle:thin:@127.0.0.1:1521:egovfrm
#Altibase
#Globals.DriverClassName=Altibase.jdbc.driver.AltibaseDriver
#Globals.Url=jdbc:Altibase://127.0.0.1:1721/egovfrm?encoding=UTF-8
#Tibero
#Globals.DriverClassName=com.tmax.tibero.jdbc.TbDriver
#Globals.Url=jdbc:tibero:thin:@127.0.0.1:1821:egovfrm
#cubrid
#Globals.DriverClassName=cubrid.jdbc.driver.CUBRIDDriver
#Globals.Url=jdbc:cubrid:127.0.0.1:33000:sht:::?charset=utf-8
#Hsql - local hssql 사용시에 적용
Globals.DriverClassName=net.sf.log4jdbc.DriverSpy
Globals.Url=jdbc:log4jdbc:hsqldb:hsql://127.0.0.1/sampledb
# MainPage Setting
Globals.MainPage = /cmm/main/mainPage.do
# 파일 확장자 화이트리스트(허용목록) : 파일 확장자를 (.)과 함께 연이어서 사용하며 (.)로 시작한다.
Globals.fileUpload.Extensions.Images = .gif.jpg.jpeg.png
Globals.fileUpload.Extensions = .gif.jpg.jpeg.png.xls.xlsx
# File ID 암호화 키
# 주의 : 반드시 기본값 "egovframe"을 다른것으로 변경하여 사용하시기 바랍니다.
Globals.File.algorithmKey = egovframe

 

설명이 잘 나와있음.

 

기본값으로 내장 DB인 hsql로 설정되어 있고, 만약 다른 DB정보를 연결하기 원한다면 아래 #으로 주석처리 되어있는 것들에 주석을 풀어서 DB 연결을 해주면 된다.

 

  • MainPage Setting : 애플리케이션이 실행될 때 기본으로 열리는 페이지가 /cmm/main/mainPage.do 로 되어있음
  • Globals.fileUpload.Extensions.Images → 이미지 업로드 허용 확장자: .gif, .jpg, .jpeg, .png
  • Globals.fileUpload.Extensions → 일반 파일 업로드 허용 확장자: .gif, .jpg, .jpeg, .png, .xls, .xlsx

        서버에서 특정 확장자의 파일만 업로드할 수 있도록 화이트리스트(허용 목록) 설정

  • Globals.File.algorithmKey = egovframe : 파일 ID를 암호화할 때 사용하는 기본 키 값(보안상 변경 필요)

 

 

3️⃣ mapper-config.xml (MyBatis 설정 파일)

MyBatis의 매퍼(Mapper) 설정 파일.

SQL 쿼리를 XML 기반으로 정의하고, Java 객체와 매핑하는 역할을 한다.


  
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!--Mybatis 설정 -->
<settings>
<!-- 전통적인 데이터베이스 컬럼명 형태인 A_COLUMN을 CamelCase형태의 자바 프로퍼티명 형태인 aColumn으로 자동으로 매핑하도록 함 -->
<setting name="mapUnderscoreToCamelCase" value="true"></setting>
<!-- 파라미터에 Null 값이 있을 경우 에러 처리 -->
<setting name="jdbcTypeForNull" value="VARCHAR"></setting>
</settings>
<!-- Type Aliases 설정-->
<typeAliases>
<typeAlias alias="egovMap" type="org.egovframe.rte.psl.dataaccess.util.EgovMap" />
<typeAlias alias="FileVO" type="egovframework.com.cmm.service.FileVO" />
<typeAlias alias="ComDefaultCodeVO" type="egovframework.com.cmm.ComDefaultCodeVO" />
<typeAlias alias="comDefaultVO" type="egovframework.com.cmm.ComDefaultVO" />
</typeAliases>
<mappers>
<!-- 이런식으로 넣는거임 -->
<!-- <mapper resource="egovframework/mapper/sample/Sample_SQL.xml"/> -->
</mappers>
</configuration>

 

마이바티스 관련해서 두가지 큰 설정이 있는데, Type Alias와 mapper 설정이다.

 

 

[Type Aliases(타입 별칭) 설정]

  • MyBatis에서 객체 타입을 짧게 사용할 수 있도록 별칭(Alias)을 정의함.
  • 예를 들어, <resultType>에서 org.egovframe.rte.psl.dataaccess.util.EgovMap을 쓰는 대신 egovMap으로 사용할 수 있음.

  
<select id="getFileList" resultType="FileVO">
SELECT * FROM FILE_TABLE;
</select>

 

 

🔹 mapper란?

  • "Java 코드에서 DB 쿼리를 실행할 수 있게 도와주는 도우미"
  • MyBatis에서 SQL 쿼리를 XML 파일이나 인터페이스에 정의하고, 이를 Java 객체와 연결하여 SQL 실행을 자동화해주는 역할을 하는 것.
  • SampleMapper_SQL.xml 파일을 예시로 들면

  
<mapper namespace="sampleMapper">
<select id="selectSampleList" resultType="egovMap">
SELECT * FROM SAMPLE
</select>
</mapper>

이런식으로 생겼고,  namespace에 있는 Mapper의 고유 이름을 통해 찾아갈 수 있다.

 

 


[<mappers> 태그 설정]

  • MyBatis에서 SQL Mapper XML 파일을 로딩하기 위한 설정.
  • 중앙 설정 파일(mapper-config.xml) 에 설정하는 것은 MyBatis의 전통적인 설정 방식으로, 모든 매퍼를 일괄 등록할 수 있다.
  • mapper-config.xml은 SqlSessionFactoryBeanconfigLocation 속성을 통해 참조된다.
  • 개별 매퍼를 수동으로 지정해야 해서, 경로가 많거나 패턴이 다양한 경우 관리가 번거로울 수 있다.

  
<mappers>
<!-- 이런식으로 넣는거임 -->
<!-- <mapper resource="egovframework/mapper/sample/Sample_SQL.xml"/> -->
</mappers>

 

 

▼ context-datasource.xml 에서 설정하는 방법(권장)

더보기

MyBatis에서 SQL 매핑파일(XML)을 등록하는 방법으로 목적은 동일하지만, 사용되는 위치가 다른 곳이 있다.

 

이방법이 유지보수에 더 적합하다고 한다.

context-mapper.xml

context-datasource.xml은 Spring Framework + MyBatis + eGovFrame 환경에서 데이터베이스 연결과 관련된 설정을 담당하는 핵심 파일이다.

이 파일이 제대로 설정되어 있어야 MyBatis 쿼리를 실행할 수 있고, DB 연결이 성공적으로 이루어짐.

 

여기서 sqlSession(SqlSessionFactoryBean)은 MyBatis가 SQL을 실행하기 위한 핵심 객체를 만들어줌.


  
<!-- Mybatis setup for Mybatis Database Layer -->
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="configLocation" value="classpath:/egovframework/mapper/config/mapper-config.xml" />
<property name="mapperLocations">
<list>
<value>classpath:/egovframework/mapper/let/**/*_${Globals.DbType}.xml</value>
<!-- 아래와 같이 xml 파일 경로 추가 -->
<value>classpath:/egovframework/mapper/let/ibsheetTemplate/*.xml</value>
</list>
</property>
</bean>

 

이것도 이런식으로 넣어주면 된다.

mapperLocations를 찾아서 아래 list에 실행하기 원하는 sql 정보가 담긴 xml 파일을 등록해줄 수 있음.

특히, 와일드카드(*)를 사용할 수 있어, 폴더 내부의 여러개의 매퍼 파일을 한번에 등록할 수 있다.

 

 

 

mapper-config.xml 에서 Mapper를 설정하나, context-datasource.xml 에서 설정하나,

정상적으로 설정되면 <mapper namespace="..."> 형태로 된 매퍼 파일들이 MyBatis에 등록된다.

항목 <mappers> (mapper-config.xml 내) mapperLocations (SqlSessionFactoryBean 내)
사용 위치 mapper-config.xml 내부 context-datasource.xml 등에서 SqlSessionFactoryBean 내
와일드카드 지원 ❌ 불가능 ✅ 가능 (*, **)
등록 방식 매퍼 파일 하나하나 명시 전체 폴더 또는 패턴 지정 가능
유지보수성 낮음 (경로 추가 시 직접 수정 필요) 높음 (패턴으로 자동 매핑 가능)
eGovFrame 권장 방식 보통 병행하거나, mapperLocations 우선 사용

 

 

 

기존 시스템의 ibatis 구조

기존 예제는 ibatis를 썼기 때문에 egovframework> sqlmap > config > hsql(각 DB별 폴더) 와 같은 구조로 되어있을꺼다.

하지만 이번 샘플은 mybatis로 사용할 것이기 때문에 구조가 다를 수 있음.

현재 mybatis는 egovframework> mapper > config > mapper-config.xml 에서 전체 관리한다.

 

 

 

 

 

 

4️⃣ let > ... > xxx_SQL_hsql.xml (HSQLDB용 SQL 매퍼 파일)

eGovFrame에서 HSQLDB 전용 SQL 쿼리를 담은 MyBatis 매퍼 파일.

폴더 구조에 따라서 분류된 각각의 역할에 맞는 SQL문이 들어있다.

화면에 보이는 EgovFile_SQL_hsql.xml 같은 파일은 File 기능과 관련된 SQL문을 모아놨을거다.

다른 EgovFile_SQL_*.xml 파일들은 각각 다른 DB (MySQL, Oracle 등)에 맞춰 작성된 것.


  
<?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="FileManageDAO">
<resultMap id="fileList" type="egovframework.com.cmm.service.FileVO">
<result property="atchFileId" column="ATCH_FILE_ID"/>
<result property="fileCn" column="FILE_CN"/>
<result property="fileExtsn" column="FILE_EXTSN"/>
<result property="fileMg" column="FILE_SIZE"/>
<result property="fileSn" column="FILE_SN"/>
<result property="fileStreCours" column="FILE_STRE_COURS"/>
<result property="orignlFileNm" column="ORIGNL_FILE_NM"/>
<result property="streFileNm" column="STRE_FILE_NM"/>
<result property="creatDt" column="CREAT_DT"/>
</resultMap>
<select id="FileManageDAO.selectFileList" parameterType="FileVO" resultMap="fileList">
SELECT
a.ATCH_FILE_ID, b.FILE_CN, b.FILE_SN, b.FILE_STRE_COURS, b.STRE_FILE_NM,
b.FILE_EXTSN, b.ORIGNL_FILE_NM, b.FILE_SIZE, a.CREAT_DT
FROM
LETTNFILE a, LETTNFILEDETAIL b
WHERE
a.ATCH_FILE_ID = #{atchFileId}
AND
a.ATCH_FILE_ID = b.ATCH_FILE_ID
AND
a.USE_AT = 'Y'
ORDER BY b.FILE_SN
</select>
...

 

그러니까 내가 뭔가를 DB의 내용을 수정하는 SQL 문을 추가한다면 여기에 폴더를 추가해서 분류해놔야한다.

 

 

 

 

 


2. SQL 파일 붙여넣고 연결하기

resources > db > shtdb.sql 부터 확인해보자.

 

Simple Homepage 실행 시 기본적으로 생성되는 DB SQL인 shtdb.sql의 내용은 아래와 같다.


  
CREATE MEMORY TABLE LETTCCMMNCLCODE(CL_CODE CHAR(3) NOT NULL PRIMARY KEY,CL_CODE_NM VARCHAR(60),CL_CODE_DC VARCHAR(200),USE_AT CHAR(1),FRST_REGIST_PNTTM TIMESTAMP,FRST_REGISTER_ID VARCHAR(20),LAST_UPDT_PNTTM TIMESTAMP,LAST_UPDUSR_ID VARCHAR(20))
CREATE MEMORY TABLE LETTCCMMNCODE(CODE_ID VARCHAR(6) NOT NULL PRIMARY KEY,CODE_ID_NM VARCHAR(60),CODE_ID_DC VARCHAR(200),USE_AT CHAR(1),CL_CODE CHAR(3),FRST_REGIST_PNTTM TIMESTAMP,FRST_REGISTER_ID VARCHAR(20),LAST_UPDT_PNTTM TIMESTAMP,LAST_UPDUSR_ID VARCHAR(20),CONSTRAINT SYS_FK_86 FOREIGN KEY(CL_CODE) REFERENCES LETTCCMMNCLCODE(CL_CODE))
CREATE MEMORY TABLE LETTCCMMNDETAILCODE(CODE_ID VARCHAR(6) NOT NULL,CODE VARCHAR(15) NOT NULL,CODE_NM VARCHAR(60),CODE_DC VARCHAR(200),USE_AT CHAR(1),FRST_REGIST_PNTTM TIMESTAMP,FRST_REGISTER_ID VARCHAR(20),LAST_UPDT_PNTTM TIMESTAMP,LAST_UPDUSR_ID VARCHAR(20),PRIMARY KEY(CODE_ID,CODE),CONSTRAINT SYS_FK_89 FOREIGN KEY(CODE_ID) REFERENCES LETTCCMMNCODE(CODE_ID))
CREATE MEMORY TABLE LETTHEMPLYRINFOCHANGEDTLS(EMPLYR_ID VARCHAR(20) NOT NULL,CHANGE_DE CHAR(20) NOT NULL,ORGNZT_ID CHAR(20),GROUP_ID CHAR(20),EMPL_NO VARCHAR(20) NOT NULL,SEXDSTN_CODE CHAR(1),BRTHDY CHAR(20),FXNUM VARCHAR(20),HOUSE_ADRES VARCHAR(100) NOT NULL,HOUSE_END_TELNO VARCHAR(4),AREA_NO VARCHAR(4),DETAIL_ADRES VARCHAR(100) NOT NULL,ZIP VARCHAR(6) NOT NULL,OFFM_TELNO VARCHAR(20),MBTLNUM VARCHAR(20) NOT NULL,EMAIL_ADRES VARCHAR(50),HOUSE_MIDDLE_TELNO VARCHAR(4),PSTINST_CODE CHAR(8),EMPLYR_STTUS_CODE VARCHAR(15) NOT NULL,ESNTL_ID CHAR(20),PRIMARY KEY(EMPLYR_ID,CHANGE_DE))
CREATE MEMORY TABLE LETTNAUTHORGROUPINFO(GROUP_ID CHAR(20) NOT NULL PRIMARY KEY,GROUP_NM VARCHAR(60) NOT NULL,GROUP_CREAT_DE CHAR(20) NOT NULL,GROUP_DC VARCHAR(100))
CREATE MEMORY TABLE LETTNAUTHORINFO(AUTHOR_CODE VARCHAR(30) NOT NULL PRIMARY KEY,AUTHOR_NM VARCHAR(60) NOT NULL,AUTHOR_DC VARCHAR(200),AUTHOR_CREAT_DE CHAR(20) NOT NULL)
CREATE MEMORY TABLE LETTNBBS(NTT_ID NUMERIC(20) NOT NULL,BBS_ID CHAR(20) NOT NULL,NTT_NO NUMERIC(20),NTT_SJ VARCHAR(2000),NTT_CN LONGVARCHAR,ANSWER_AT CHAR(1),PARNTSCTT_NO NUMERIC(10),ANSWER_LC INTEGER,SORT_ORDR NUMERIC(8),RDCNT NUMERIC(10),USE_AT CHAR(1) NOT NULL,NTCE_BGNDE CHAR(20),NTCE_ENDDE CHAR(20),NTCR_ID VARCHAR(20),NTCR_NM VARCHAR(20),PASSWORD VARCHAR(200),ATCH_FILE_ID CHAR(20),FRST_REGIST_PNTTM TIMESTAMP NOT NULL,FRST_REGISTER_ID VARCHAR(20) NOT NULL,LAST_UPDT_PNTTM TIMESTAMP,LAST_UPDUSR_ID VARCHAR(20),PRIMARY KEY(NTT_ID,BBS_ID))
CREATE MEMORY TABLE LETTNBBSMASTER(BBS_ID CHAR(20) NOT NULL PRIMARY KEY,BBS_NM VARCHAR(255) NOT NULL,BBS_INTRCN VARCHAR(2400),BBS_TY_CODE CHAR(6) NOT NULL,BBS_ATTRB_CODE CHAR(6) NOT NULL,REPLY_POSBL_AT CHAR(1),FILE_ATCH_POSBL_AT CHAR(1) NOT NULL,ATCH_POSBL_FILE_NUMBER NUMERIC(2) NOT NULL,ATCH_POSBL_FILE_SIZE NUMERIC(8),USE_AT CHAR(1) NOT NULL,TMPLAT_ID CHAR(20),FRST_REGISTER_ID VARCHAR(20) NOT NULL,FRST_REGIST_PNTTM TIMESTAMP NOT NULL,LAST_UPDUSR_ID VARCHAR(20),LAST_UPDT_PNTTM TIMESTAMP)
CREATE MEMORY TABLE LETTNBBSMASTEROPTN(BBS_ID CHAR(20) DEFAULT '' NOT NULL PRIMARY KEY,ANSWER_AT CHAR(1) DEFAULT '' NOT NULL,STSFDG_AT CHAR(1) DEFAULT '' NOT NULL,FRST_REGIST_PNTTM TIMESTAMP DEFAULT '1970-01-01 00:00:00.0' NOT NULL,LAST_UPDT_PNTTM TIMESTAMP,FRST_REGISTER_ID VARCHAR(20) DEFAULT '' NOT NULL,LAST_UPDUSR_ID VARCHAR(20))
CREATE MEMORY TABLE LETTNBBSUSE(BBS_ID CHAR(20) NOT NULL,TRGET_ID CHAR(20) NOT NULL,USE_AT CHAR(1) NOT NULL,REGIST_SE_CODE CHAR(6),FRST_REGIST_PNTTM TIMESTAMP,FRST_REGISTER_ID VARCHAR(20) NOT NULL,LAST_UPDT_PNTTM TIMESTAMP,LAST_UPDUSR_ID VARCHAR(20),PRIMARY KEY(BBS_ID,TRGET_ID),CONSTRAINT SYS_FK_98 FOREIGN KEY(BBS_ID) REFERENCES LETTNBBSMASTER(BBS_ID))
CREATE MEMORY TABLE LETTNEMPLYRINFO(EMPLYR_ID VARCHAR(20) NOT NULL PRIMARY KEY,ORGNZT_ID CHAR(20),USER_NM VARCHAR(60) NOT NULL,PASSWORD VARCHAR(200) NOT NULL,EMPL_NO VARCHAR(20),IHIDNUM VARCHAR(13),SEXDSTN_CODE CHAR(1),BRTHDY CHAR(20),FXNUM VARCHAR(20),HOUSE_ADRES VARCHAR(100) NOT NULL,PASSWORD_HINT VARCHAR(100) NOT NULL,PASSWORD_CNSR VARCHAR(100) NOT NULL,HOUSE_END_TELNO VARCHAR(4) NOT NULL,AREA_NO VARCHAR(4) NOT NULL,DETAIL_ADRES VARCHAR(100),ZIP VARCHAR(6) NOT NULL,OFFM_TELNO VARCHAR(20),MBTLNUM VARCHAR(20) NOT NULL,EMAIL_ADRES VARCHAR(50),OFCPS_NM VARCHAR(60),HOUSE_MIDDLE_TELNO VARCHAR(4) NOT NULL,GROUP_ID CHAR(20),PSTINST_CODE CHAR(8),EMPLYR_STTUS_CODE VARCHAR(15) NOT NULL,ESNTL_ID CHAR(20) NOT NULL,CRTFC_DN_VALUE VARCHAR(20),SBSCRB_DE TIMESTAMP,CONSTRAINT SYS_FK_101 FOREIGN KEY(GROUP_ID) REFERENCES LETTNAUTHORGROUPINFO(GROUP_ID) ON DELETE CASCADE)
CREATE MEMORY TABLE LETTNEMPLYRSCRTYESTBS(SCRTY_DTRMN_TRGET_ID VARCHAR(20) NOT NULL PRIMARY KEY,MBER_TY_CODE VARCHAR(15),AUTHOR_CODE VARCHAR(30) NOT NULL,CONSTRAINT SYS_FK_104 FOREIGN KEY(SCRTY_DTRMN_TRGET_ID) REFERENCES LETTNEMPLYRINFO(EMPLYR_ID),CONSTRAINT SYS_FK_113 FOREIGN KEY(AUTHOR_CODE) REFERENCES LETTNAUTHORINFO(AUTHOR_CODE))
CREATE MEMORY TABLE LETTNENTRPRSMBER(ENTRPRS_MBER_ID VARCHAR(20) NOT NULL PRIMARY KEY,ENTRPRS_SE_CODE CHAR(15),BIZRNO VARCHAR(10),JURIRNO VARCHAR(13),CMPNY_NM VARCHAR(60) NOT NULL,CXFC VARCHAR(50),ZIP VARCHAR(6) NOT NULL,ADRES VARCHAR(100) NOT NULL,ENTRPRS_MIDDLE_TELNO VARCHAR(4) NOT NULL,FXNUM VARCHAR(20),INDUTY_CODE CHAR(15),APPLCNT_NM VARCHAR(50) NOT NULL,APPLCNT_IHIDNUM VARCHAR(13),SBSCRB_DE TIMESTAMP,ENTRPRS_MBER_STTUS VARCHAR(15),ENTRPRS_MBER_PASSWORD VARCHAR(200),ENTRPRS_MBER_PASSWORD_HINT VARCHAR(100) NOT NULL,ENTRPRS_MBER_PASSWORD_CNSR VARCHAR(100) NOT NULL,GROUP_ID CHAR(20),DETAIL_ADRES VARCHAR(100),ENTRPRS_END_TELNO VARCHAR(4) NOT NULL,AREA_NO VARCHAR(4) NOT NULL,APPLCNT_EMAIL_ADRES VARCHAR(50) NOT NULL,ESNTL_ID CHAR(20) NOT NULL,CONSTRAINT SYS_FK_116 FOREIGN KEY(GROUP_ID) REFERENCES LETTNAUTHORGROUPINFO(GROUP_ID) ON DELETE CASCADE)
CREATE MEMORY TABLE LETTNFILE(ATCH_FILE_ID CHAR(20) NOT NULL PRIMARY KEY,CREAT_DT TIMESTAMP NOT NULL,USE_AT CHAR(1))
CREATE MEMORY TABLE LETTNFILEDETAIL(ATCH_FILE_ID CHAR(20) NOT NULL,FILE_SN NUMERIC(10) NOT NULL,FILE_STRE_COURS VARCHAR(2000) NOT NULL,STRE_FILE_NM VARCHAR(255) NOT NULL,ORIGNL_FILE_NM VARCHAR(255),FILE_EXTSN VARCHAR(20) NOT NULL,FILE_CN LONGVARCHAR,FILE_SIZE NUMERIC(8),PRIMARY KEY(ATCH_FILE_ID,FILE_SN),CONSTRAINT SYS_FK_119 FOREIGN KEY(ATCH_FILE_ID) REFERENCES LETTNFILE(ATCH_FILE_ID))
CREATE MEMORY TABLE LETTNGNRLMBER(MBER_ID VARCHAR(20) NOT NULL PRIMARY KEY,PASSWORD VARCHAR(200) NOT NULL,PASSWORD_HINT VARCHAR(100),PASSWORD_CNSR VARCHAR(100),IHIDNUM VARCHAR(13),MBER_NM VARCHAR(50) NOT NULL,ZIP VARCHAR(6) NOT NULL,ADRES VARCHAR(100) NOT NULL,AREA_NO VARCHAR(4) NOT NULL,MBER_STTUS VARCHAR(15),DETAIL_ADRES VARCHAR(100),END_TELNO VARCHAR(4) NOT NULL,MBTLNUM VARCHAR(20) NOT NULL,GROUP_ID CHAR(20),MBER_FXNUM VARCHAR(20),MBER_EMAIL_ADRES VARCHAR(50),MIDDLE_TELNO VARCHAR(4) NOT NULL,SBSCRB_DE TIMESTAMP,SEXDSTN_CODE CHAR(1),ESNTL_ID CHAR(20) NOT NULL,CONSTRAINT SYS_FK_122 FOREIGN KEY(GROUP_ID) REFERENCES LETTNAUTHORGROUPINFO(GROUP_ID) ON DELETE CASCADE)
CREATE MEMORY TABLE LETTNORGNZTINFO(ORGNZT_ID CHAR(20) NOT NULL PRIMARY KEY,ORGNZT_NM VARCHAR(20) NOT NULL,ORGNZT_DC VARCHAR(100))
CREATE MEMORY TABLE LETTNSCHDULINFO(SCHDUL_ID CHAR(20) NOT NULL PRIMARY KEY,SCHDUL_SE CHAR(1),SCHDUL_DEPT_ID VARCHAR(20),SCHDUL_KND_CODE VARCHAR(20),SCHDUL_BEGINDE TIMESTAMP,SCHDUL_ENDDE TIMESTAMP,SCHDUL_NM VARCHAR(255),SCHDUL_CN VARCHAR(2500),SCHDUL_PLACE VARCHAR(255),SCHDUL_IPCR_CODE CHAR(1),SCHDUL_CHARGER_ID VARCHAR(20),ATCH_FILE_ID CHAR(20),FRST_REGIST_PNTTM TIMESTAMP,FRST_REGISTER_ID VARCHAR(20),LAST_UPDT_PNTTM TIMESTAMP,LAST_UPDUSR_ID VARCHAR(20),REPTIT_SE_CODE CHAR(3))
CREATE MEMORY TABLE LETTNTMPLATINFO(TMPLAT_ID CHAR(20) NOT NULL PRIMARY KEY,TMPLAT_NM VARCHAR(255),TMPLAT_COURS VARCHAR(2000),USE_AT CHAR(1),TMPLAT_SE_CODE CHAR(6),FRST_REGISTER_ID VARCHAR(20),FRST_REGIST_PNTTM TIMESTAMP,LAST_UPDUSR_ID VARCHAR(20),LAST_UPDT_PNTTM TIMESTAMP)
CREATE MEMORY TABLE IDS(TABLE_NAME VARCHAR(20) DEFAULT '' NOT NULL PRIMARY KEY,NEXT_ID NUMERIC(30) DEFAULT 0 NOT NULL)
ALTER TABLE LETTHEMPLYRINFOCHANGEDTLS ADD CONSTRAINT SYS_FK_92 FOREIGN KEY(EMPLYR_ID) REFERENCES LETTNEMPLYRINFO(EMPLYR_ID)
ALTER TABLE LETTNBBS ADD CONSTRAINT SYS_FK_95 FOREIGN KEY(BBS_ID) REFERENCES LETTNBBSMASTER(BBS_ID)
ALTER TABLE LETTNEMPLYRSCRTYESTBS ADD CONSTRAINT SYS_FK_107 FOREIGN KEY(SCRTY_DTRMN_TRGET_ID) REFERENCES LETTNENTRPRSMBER(ENTRPRS_MBER_ID)
ALTER TABLE LETTNEMPLYRSCRTYESTBS ADD CONSTRAINT SYS_FK_110 FOREIGN KEY(SCRTY_DTRMN_TRGET_ID) REFERENCES LETTNGNRLMBER(MBER_ID)
SET WRITE_DELAY 20
SET SCHEMA PUBLIC
INSERT INTO LETTCCMMNCLCODE VALUES('LET','전자정부 프레임워크 경량환경 템플릿','전자정부 프레임워크 경량환경 템플릿','Y','2011-08-31 00:00:00.000000000','SYSTEM','2011-08-31 00:00:00.000000000','SYSTEM')
INSERT INTO LETTCCMMNCODE VALUES('COM001','등록구분','게시판, 커뮤니티, 동호회 등록구분코드','Y','LET','2011-08-31 00:00:00.000000000','SYSTEM','2011-08-31 00:00:00.000000000','SYSTEM')
INSERT INTO LETTCCMMNCODE VALUES('COM003','업무구분','업무구분코드','Y','LET','2011-08-31 00:00:00.000000000','SYSTEM','2011-08-31 00:00:00.000000000','SYSTEM')
INSERT INTO LETTCCMMNCODE VALUES('COM004','게시판유형','게시판유형구분코드','Y','LET','2011-08-31 00:00:00.000000000','SYSTEM','2011-08-31 00:00:00.000000000','SYSTEM')
INSERT INTO LETTCCMMNCODE VALUES('COM005','템플릿유형','템플릿유형구분코드','Y','LET','2011-08-31 00:00:00.000000000','SYSTEM','2011-08-31 00:00:00.000000000','SYSTEM')
INSERT INTO LETTCCMMNCODE VALUES('COM009','게시판속성','게시판 속성','Y','LET','2011-08-31 00:00:00.000000000','SYSTEM','2011-08-31 00:00:00.000000000','SYSTEM')
INSERT INTO LETTCCMMNCODE VALUES('COM019','일정중요도','일정중요도 낮음/보통/높음 상태구분','Y','LET','2011-08-31 00:00:00.000000000','SYSTEM','2011-08-31 00:00:00.000000000','SYSTEM')
INSERT INTO LETTCCMMNCODE VALUES('COM030','일정구분','일정구분 코드','Y','LET','2011-08-31 00:00:00.000000000','SYSTEM','2011-08-31 00:00:00.000000000','SYSTEM')
INSERT INTO LETTCCMMNCODE VALUES('COM031','반복구분','일정 반복구분 코드','Y','LET','2011-08-31 00:00:00.000000000','SYSTEM','2011-08-31 00:00:00.000000000','SYSTEM')
INSERT INTO LETTCCMMNDETAILCODE VALUES('COM001','REGC01','단일 게시판 이용등록','단일 게시판 이용등록','Y','2011-08-31 00:00:00.000000000','SYSTEM','2011-08-31 00:00:00.000000000','SYSTEM')
INSERT INTO LETTCCMMNDETAILCODE VALUES('COM001','REGC07','게시판사용자등록','게시판사용자등록','Y','2011-08-31 00:00:00.000000000','SYSTEM','2011-08-31 00:00:00.000000000','SYSTEM')
INSERT INTO LETTCCMMNDETAILCODE VALUES('COM003','BBS','게시판','게시판','Y','2011-08-31 00:00:00.000000000','SYSTEM','2011-08-31 00:00:00.000000000','SYSTEM')
INSERT INTO LETTCCMMNDETAILCODE VALUES('COM004','BBST01','일반게시판','일반게시판','Y','2011-08-31 00:00:00.000000000','SYSTEM','2011-08-31 00:00:00.000000000','SYSTEM')
INSERT INTO LETTCCMMNDETAILCODE VALUES('COM004','BBST02','익명게시판','익명게시판','Y','2011-08-31 00:00:00.000000000','SYSTEM','2011-08-31 00:00:00.000000000','SYSTEM')
INSERT INTO LETTCCMMNDETAILCODE VALUES('COM004','BBST03','공지게시판','공지게시판','Y','2011-08-31 00:00:00.000000000','SYSTEM','2011-08-31 00:00:00.000000000','SYSTEM')
INSERT INTO LETTCCMMNDETAILCODE VALUES('COM004','BBST04','방명록','방명록','Y','2011-08-31 00:00:00.000000000','SYSTEM','2011-08-31 00:00:00.000000000','SYSTEM')
INSERT INTO LETTCCMMNDETAILCODE VALUES('COM005','TMPT01','게시판템플릿','게시판템플릿','Y','2011-08-31 00:00:00.000000000','SYSTEM','2011-08-31 00:00:00.000000000','SYSTEM')
INSERT INTO LETTCCMMNDETAILCODE VALUES('COM009','BBSA01','유효게시판','유효게시판','Y','2011-08-31 00:00:00.000000000','SYSTEM','2011-08-31 00:00:00.000000000','SYSTEM')
INSERT INTO LETTCCMMNDETAILCODE VALUES('COM009','BBSA02','갤러리','갤러리','Y','2011-08-31 00:00:00.000000000','SYSTEM','2011-08-31 00:00:00.000000000','SYSTEM')
INSERT INTO LETTCCMMNDETAILCODE VALUES('COM009','BBSA03','일반게시판','일반게시판','Y','2011-08-31 00:00:00.000000000','SYSTEM','2011-08-31 00:00:00.000000000','SYSTEM')
INSERT INTO LETTCCMMNDETAILCODE VALUES('COM019','A','높음','높음','Y','2011-08-31 00:00:00.000000000','SYSTEM','2011-08-31 00:00:00.000000000','SYSTEM')
INSERT INTO LETTCCMMNDETAILCODE VALUES('COM019','B','보통','보통','Y','2011-08-31 00:00:00.000000000','SYSTEM','2011-08-31 00:00:00.000000000','SYSTEM')
INSERT INTO LETTCCMMNDETAILCODE VALUES('COM019','C','낮음','낮음','Y','2011-08-31 00:00:00.000000000','SYSTEM','2011-08-31 00:00:00.000000000','SYSTEM')
INSERT INTO LETTCCMMNDETAILCODE VALUES('COM030','1','회의','회의','Y','2011-08-31 00:00:00.000000000','SYSTEM','2011-08-31 00:00:00.000000000','SYSTEM')
INSERT INTO LETTCCMMNDETAILCODE VALUES('COM030','2','세미나','세미나','Y','2011-08-31 00:00:00.000000000','SYSTEM','2011-08-31 00:00:00.000000000','SYSTEM')
INSERT INTO LETTCCMMNDETAILCODE VALUES('COM030','3','강의','강의','Y','2011-08-31 00:00:00.000000000','SYSTEM','2011-08-31 00:00:00.000000000','SYSTEM')
INSERT INTO LETTCCMMNDETAILCODE VALUES('COM030','4','교육','교육','Y','2011-08-31 00:00:00.000000000','SYSTEM','2011-08-31 00:00:00.000000000','SYSTEM')
INSERT INTO LETTCCMMNDETAILCODE VALUES('COM030','5','기타','기타','Y','2011-08-31 00:00:00.000000000','SYSTEM','2011-08-31 00:00:00.000000000','SYSTEM')
INSERT INTO LETTCCMMNDETAILCODE VALUES('COM031','1','당일','당일','Y','2011-08-31 00:00:00.000000000','SYSTEM','2011-08-31 00:00:00.000000000','SYSTEM')
INSERT INTO LETTCCMMNDETAILCODE VALUES('COM031','2','반복','반복','Y','2011-08-31 00:00:00.000000000','SYSTEM','2011-08-31 00:00:00.000000000','SYSTEM')
INSERT INTO LETTCCMMNDETAILCODE VALUES('COM031','3','연속','연속','Y','2011-08-31 00:00:00.000000000','SYSTEM','2011-08-31 00:00:00.000000000','SYSTEM')
INSERT INTO LETTCCMMNDETAILCODE VALUES('COM031','4','요일반복','요일반복','Y','2011-08-31 00:00:00.000000000','SYSTEM','2011-08-31 00:00:00.000000000','SYSTEM')
INSERT INTO LETTNORGNZTINFO VALUES ('ORGNZT_0000000000000','관리자부서','관리자부서')
INSERT INTO LETTNAUTHORGROUPINFO VALUES('GROUP_00000000000000','기본 그룹입니다','2011-08-31','기본 그룹')
INSERT INTO LETTNBBSMASTER VALUES('BBSMSTR_AAAAAAAAAAAA','공지사항','공지사항게시판','BBST03','BBSA03','Y','Y',2,5242880,'Y','TMPLAT_BOARD_DEFAULT','USRCNFRM_00000000000','2011-08-31 12:00:00.000000000','USRCNFRM_00000000000','2011-08-31 12:00:00.000000000')
INSERT INTO LETTNBBSMASTER VALUES('BBSMSTR_BBBBBBBBBBBB','갤러리','갤러리게시판','BBST01','BBSA02','Y','Y',2,5242880,'Y','TMPLAT_BOARD_DEFAULT','USRCNFRM_00000000000','2011-08-31 12:00:00.000000000','USRCNFRM_00000000000','2011-08-31 12:00:00.000000000')
INSERT INTO LETTNBBSUSE VALUES('BBSMSTR_AAAAAAAAAAAA','SYSTEM_DEFAULT_BOARD','Y','REGC01','2011-08-31 12:00:00.000000000','USRCNFRM_00000000000','2011-08-31 12:00:00.000000000','USRCNFRM_00000000000')
INSERT INTO LETTNBBSUSE VALUES('BBSMSTR_BBBBBBBBBBBB','SYSTEM_DEFAULT_BOARD','Y','REGC01','2011-08-31 12:00:00.000000000','USRCNFRM_00000000000','2011-08-31 12:00:00.000000000','USRCNFRM_00000000000')
INSERT INTO LETTNEMPLYRINFO VALUES('admin','ORGNZT_0000000000000','관리자','JfQ7FIatlaE5jj7rPYO8QBABX8yb7bNbQy4AKY1QIfc=','','','F','','','관리자 주소','','','','','','','','','','','','GROUP_00000000000000','00000000','P','USRCNFRM_00000000000','','2011-08-31 00:00:00.000000000')
INSERT INTO LETTNTMPLATINFO VALUES('TMPLAT_BOARD_DEFAULT','게시판 기본템플릿','/css/egovframework/cop/bbs/egovbbsTemplate.css','Y','TMPT01','SYSTEM','2011-08-31 00:00:00.000000000',NULL,NULL)
INSERT INTO IDS VALUES('BBS_ID',1)
INSERT INTO IDS VALUES('FILE_ID',1)
INSERT INTO IDS VALUES('SAMPLE',1)
INSERT INTO IDS VALUES('SCHDUL_ID',1)
INSERT INTO IDS VALUES('TMPLAT_ID',1)

 

전체 DB 초기화용 HSQLDB용 DDL과 일부 데이터 삽입 구문이 포함되어 있고, 테이블 구성은 아래와 같다.

 

✅ 주요 테이블 구성

공통 코드

  • LETTCCMMNCLCODE: 공통분류코드
  • LETTCCMMNCODE: 공통코드
  • LETTCCMMNDETAILCODE: 공통상세코드

게시판 관련

  • LETTNBBSMASTER: 게시판 마스터 (게시판 설정)
  • LETTNBBSMASTEROPTN: 게시판 옵션
  • LETTNBBSUSE: 게시판 사용정보 (조직별)
  • LETTNBBS: 게시글 테이블

사용자 관련

  • LETTNEMPLYRINFO: 일반 사용자(직원) 정보
  • LETTNEMPLYRSCRTYESTBS: 사용자-권한 매핑 테이블 (보안 설정)
  • LETTNAUTHORGROUPINFO: 권한 그룹 정보
  • LETTNAUTHORINFO: 권한 정보
  • LETTHEMPLYRINFOCHANGEDTLS: 사용자 변경 이력

회원 유형별 테이블

  • LETTNENTRPRSMBER: 기업 회원
  • LETTNGNRLMBER: 일반 회원

기타 테이블

  • LETTNFILE, LETTNFILEDETAIL: 파일 첨부 관련
  • LETTNSCHDULINFO: 일정 정보
  • LETTNTMPLATINFO: 템플릿 정보
  • LETTNORGNZTINFO: 조직 정보
  • IDS: ID 생성기 (NEXT_ID 저장용)

 

✅ 주요 제약 조건 및 외래키

  • 대부분의 테이블은 GROUP_ID, AUTHOR_CODE, BBS_ID 등을 통해 서로 연결됨
  • ON DELETE CASCADE가 있는 경우 하위 레코드 자동 삭제됨 (예: GROUP_ID FK)
  • 일부 테이블은 여러 테이블에서 FOREIGN KEY로 참조됨 (예: SCRTY_DTRMN_TRGET_ID는 다중 참조)

 

✅ 테이블 관계도 요약 (일부)


  
[LETTNBBS] ──▶ [LETTNBBSMASTER]
└─▶ [LETTNBBSMASTEROPTN]
└─▶ [LETTNBBSUSE]
[LETTNEMPLYRINFO] ──▶ [LETTNAUTHORGROUPINFO]
│ ▲
└─▶ [LETTNEMPLYRSCRTYESTBS] ──▶ [LETTNAUTHORINFO]
└─▶ [LETTNENTRPRSMBER]
└─▶ [LETTNGNRLMBER]
[LETTCCMMNCODE] ──▶ [LETTCCMMNCLCODE]
[LETTCCMMNDETAILCODE] ──▶ [LETTCCMMNCODE]

 

 

shtdb.sql은 냅두고 동일한 경로에 샘플 DB를 넣어준다.

 

.sql.mv.db 로 끝나는 파일도 같이 있는데, 로컬에서 작성한 샘플 데이터, 설정 정보 등을 그대로 다른 환경에서도 사용하고 싶을 때 같이 옮겨줘야 한다.

 

  • .mv.db    ->   "Multi-Version DB" 형식의 물리적 데이터 파일
  • .sql.trace.db   -> 트랜잭션 로그 파일로, 비정상 종료(예: 전원 꺼짐, 강제 종료 등) 시 복구를 도와줌.

요렇게 경로에 전부 옮겨준다.

 

이외에 또 다른 점을 보자면 mappers  아래에 template의 SQL 파일이 따로 있는데, 하위 폴더 만들어서 넣어준다.

폴더 만들어서 넣어줌

 

여기엔 아래와 같이 CRUD에 필요한 구체적인 SQL문이 들어있음.


  
<select id="selectDept" resultType="egovMap" parameterType="java.util.HashMap">
<![CDATA[
/** selectDept */
SELECT * FROM DEPT WHERE 1=1
]]>
<if test="loc != null ">
AND LOC LIKE '%'||#{loc}||'%'
</if>
<if test="deptno != null ">
AND DEPTNO = #{deptno}
</if>
</select>

 

이외에 기존 시스템과 다른 몇가지 파일들이 있는데,

기타 설정파일들

 

global.properties 보면 JDBC URL에 아래와 같이 써있다.


  
Globals.Url=jdbc:log4jdbc:hsqldb:hsql://127.0.0.1/sampledb

HSQLDB의 JDBC URL 패턴에 따라 모드가 다르다.

 

보면 지금 기본적으로 서버모드로 되어있음.

모드 JDBC URL 형식 설명
메모리 모드 jdbc:hsqldb:mem:mydb 메모리에서 실행됨. 프로그램 종료 시 데이터 사라짐.
파일 모드 jdbc:hsqldb:file:/path/to/db 파일 기반 DB. 종료 후에도 데이터 유지됨.
서버 모드 jdbc:hsqldb:hsql://host/dbname 네트워크 서버 방식. 여러 클라이언트가 접속 가능.

 

📌 서버 모드(Server Mode)란?

  • HSQLDB를 서버 프로세스로 실행하고, 클라이언트가 네트워크를 통해 접속하는 방식.
  • 데이터가 파일에 저장되므로 종료 후에도 유지됨.
  • 동시에 여러 애플리케이션이 같은 DB에 접근 가능함.
  • 보통 독립적인 HSQLDB 서버를 실행한 후 클라이언트가 접속하는 방식.

 

📌 서버 모드에서 실행 방법

  1. HSQLDB 서버를 실행해야 함.

  
java -cp hsqldb.jar org.hsqldb.server.Server --database.0 file:sampledb --dbname.0 sampledb

 

  1. 터미널로 hsqlbb.jar 있는 경로 들어가서 위 명령어를 실행하면 sampledb.script, sampledb.properties 같은 파일이 생성됨.
  2. 이후 jdbc:hsqldb:hsql://localhost/sampledb로 접속하면 정상 동작.

pom.xml에 이렇게 들어있는데, Maven이 자동으로 target/lib 또는 .m2/repository에 저장한다.


  
<dependency>
<groupId>org.hsqldb</groupId> <!-- HSQLDB 라이브러리 그룹 ID -->
<artifactId>hsqldb</artifactId> <!-- 라이브러리 이름 -->
<version>2.7.2</version> <!-- 사용하는 HSQLDB 버전 (최신 버전) -->
<classifier>jdk8</classifier> <!-- JDK 8 전용 버전 사용 -->
</dependency>

 

어쨌든 이렇게 하면 db 관련 설정파일이 생성된다.

  • db.properties (데이터베이스 설정 파일)
  • db.script (HSQLDB용 SQL 초기화 스크립트)

하지만 난 기존꺼 그냥 붙여넣었고.... 그냥 임베디드 모드(메모리모드)를 사용할 예정이다.

 

여기까지 resource 부분은 다 봤고 이 다음에는 java 파일을 설정해보자.

반응형
LIST
반응형
SMALL

리눅스에서 jupyter notebook를 실행하고 윈도우에 설치된 오라클 11g와 연동하는 것까지 해보았다.

 

▼저번 포스팅에서 해본 파이썬 - 오라클 연동 정리(링크)

더보기

방법을 간단히 요약하자면,

 

1) 오라클 connection 연결

2) connection이 cursor 객체 가져옴(연결도구)

3) cursor 객체의 fetch메서드를 이용해서 데이터를 서버로부터 가져온다.

4) cursor 객체의  execute() 메서드를 사용해서 파이썬에 입력한 SQL 문장을 오라클 DB 서버로 보낸다.

5) 주고받는 것이 완료되면 connection 객체의 commit() 메서드를 사용해서 데이터를 확정한다.

6) connection.close()를 해서 오라클과 연결을 끊는다.

 

즉, 정리하자면

오라클 수입 : import cx_Oracle as ora
오라클 Connection 연결 : conn = ora.connect('kosmorpa/test00@192.168.0.122:1522/orcl')
Cursor 불러오기 :  cursor = conn.cursor()
a) SQL 입력문장 -> 오라클 DB : cursor.execute( 전송할 sql 문장 )
b) 오라클 DB 데이터 -> 파이썬 : cursor.fetch*****()
Cursor 닫기 : cursor.close()
오라클 DB 저장 : conn.commit()
오라클 연결 끊기 : conn.close()

이걸 이용해서 파이참(PyCharm)에서 DB를 원래 했던 sqlite가 아닌 윈도우에 설치 되어있는 오라클과 연동해서 해보도록 하자.


 

준비된 템플릿에 회원가입 창과 아이디 중복 확인 프로그램을 만들었었다.

 

이번엔 로그인 앱을 만들어 web1과 연결해보자.

 

## web1에 있는 static 파일을 이용해서 추후 web1에 템플릿 디자인으로 적용해보기.

 


[ login/logout 웹 페이지 기능 구현하기 ]


1. login 앱 만들기

늘 하던대로 login > templates > login > index.html 파일 생성


  
python manage.py startapp login

 

2. index.html 파일 만들기

회원가입(앞선 포스팅 web1에서 만들었던 tripmember를 링크)과 로그인 폼에는 링크를 걸어두었다.

서버 구동을 확인한 후 로그인 폼(loginform)을 만들러 갈 예정.


  
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>index.html</title>
<style>
ol, ul {list-style:none;}
ul li { float:left; margin-left:2px; background:pink}
</style>
</head>
<body>
<h1>로그인/로그아웃 데모</h1>
<span>추후 web1에 템플릿 디자인으로 적용하세요.</span>
<nav id="menu">
<header class="major">
<ul>
<li><a href="main">Home</a> </li>
<li><a href="survey">설문조사</a> </li>
<li>김길동님 반갑습니다. </li>
<li><a href="logout">로그아웃</a> </li>
<li><a href="/web1/tripmember">회원가입</a> </li>
<li><a href="loginform">로그인폼</a> </li>
</ul>
</header>
</nav>
</body>
</html>

3. 뷰 추가(view.py)

이제 이쯤이야 쉽다. 첫페이지 추가

4. urls.py 만들고 내용 추가

5. config/setting 추가

6. config/urls 추가

7. 서버 구동 및 index.html 파일 열기

대충 이런모양이다.

기본 세팅 끝.


본격적으로 첫 페이지의 로그인 폼을 만들러 가자.


 

8. login.html 만들기

index에 web1에서 썼었던 로그인폼 그대로 가져와서 내용 추가하자.


  
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>login.html</title>
<style>
ol, ul {list-style:none;}
ul li { float:left; margin-left:2px; background:pink}
</style>
</head>
<body>
<h1>로그인 페이지</h1>
<span>추후 web1에 템플릿 디자인으로 적용하세요.</span>
<nav id="menu">
<section id="main">
<header>
<h2>StarTrip</h2>
<form method="post" action="">
{% csrf_token %}
<div class="fields">
<div class="field">
<input type="text" name="id" id="id" value="" placeholder="ID"
style="text-transform :lowercase"/>
</div>
<div class="field">
<input type="password" name="pwd" id="pwd" placeholder="Password" />
</div>
</div>
<ul class="actions special">
<li><input type="submit" value="로그인" class="primary"/></li>
</ul>
<div><span style="color:red;">{{error}}</span> </div>
</form>
</header>
</section>
</nav>
</body>
</html>

 

9. views.py로 와서 추가

login.html를 loginform 이름으로 정의하자.

여기서는 if문을 사용해서 loginform에 대해 1) 전송방식 구분 2) Session 유무에 따른 구분 을 두자.

 

▼왜 갑자기 세션이 나와?

더보기

갑자기 세션이 등장했다.(두둥탁)

JSP에서 쓰던 Session에 대한 개념을 여기에 정리해보고자 한다.

 

원래 HTTP는 비연결성 프로토콜(stateless)이기 때문에 전단계의 데이터 요청과 다음 데이터 요청이 관련이 없다. 이렇게 되면 항상 새롭게 매번, 데이터를 요청하고 받아와야 하기 때문에, 세션과 쿠키가 등장했다. 세션은 웹 컨테이너, 쿠키는 웹 브라우저에 상태 유지를 위한 정보를 저장한다.

 

예시를 들어보자.

원래 회원가입 절차는 사용자가 무조건 각 단계를 순차적으로 넘어와야 한다. HTTP로 구현을 하게 되면 모든 페이지에 대해서 URL(/step1/, /step2/, /step3/)로 접근할 수 있다. 하지만 세션을 사용하면 각 정보를 저장하기 때문에 /step1/ => /step2/ => /step3/ 순서로만 접근이 가능하게 바꿀 수 있다. 각 단계의 순서대로 접근하지 않으면 403 Forbidden 페이지를 볼 수 있을 것이다.

 

Django에서는 기본적으로 세션 관리 기능을 제공해주고, request.session 이라는 딕셔너리 객체에 필요한 값을 저장하고 읽어올 수 있다. view.py 에서 return을 걸어주기 전에 if문을 통해 step1에서 step2로만 가게끔(나머지는 false가 되게끔) 만들면 접근 순서를 제어할 수 있다. (출처)


▽코드 문장으로 읽기

더보기

loginform 두가지 if문이 중첩되어 있다.

1) 만약 user_id 가 request.session(=딕셔너리 객체에 필요한 값을 저장하고 읽어옴) 안에 있다면 /login 페이지로 이동해라.

2) 만약 요청방법이(로그인 폼에서 submit 버튼 누르고 id 비번 값이 넘어가는는 방법이) POST이면?

   n -> login/login.html로 가라.

   y -> post 방식으로 id를 요청해서 user_id 안에 넣어라.(비밀번호도) 그 후에 print해라.

   (db_id와 db_pwd 는 아직 db 연동을 하지 않았기 때문에 임의로 정해놓았음)

   2-1) 그 후 또다른 if문

      만약 db 안에 user 의 아이디와 비밀번호가 있다면? y -> 로그인 성공 띄우고 /login으로 가라.

      n -> 로그인 실패 띄우고 에러메시지 띄우고,  login/login.html로 가라.



  
from django.shortcuts import render, redirect
# Create your views here.
from django.views.decorators.csrf import csrf_protect
def home(request):
return render(request, "login/index.html")
# 1. 전송방식 구분
# 2. Session 유무에 따른 구분
@csrf_protect
def ldginform(request):
# 세션에 등록한 값이 존재하는가?
if 'user_id' in request.session:
return redirect('/login')
if request.method == 'POST':
user_id = request.POST['id']
user_pwd = request.POST['pwd']
print('user_id: ', user_id)
print('user_pwd: ', user_pwd)
db_id = 'xman'
db_pwd = '11'
# 중첩 if 문
if user_id in db_id and user_pwd in db_pwd:
print('로그인 성공')
request.session['user_id'] = db_id
request.session['user_name'] = '김길동'
return redirect('/login')
else:
print('로그인 실패')
msg = '아이디나 비밀번호가 잘못되었습니다.'
return render(request,"login/login.html",{"error":msg})
return render(request, "login/login.html")

10. 로그아웃도 추가(view.py)

세션을 삭제하고 나간다.


  
# 로그아웃 처리
def logout(request):
del request.session['user_id']
del request.session['user_name']
return redirect('/login')

당연히 view에 정의했으니 길 터줘야 한다.

11. urls.py에 path 추가

간-단

12. 세션 나눠주기(index.html)

views.py에서 if문으로 상황에 따라 경로를 만들어주었다.

첫 페이지인 index.html에서도 각 부분에 따른 세션을 나눠줘야 한다. 각각 if / else를 나눠 표시해주자.

왜??


  
<ul>
<li><a href="main">Home</a> </li>
<li><a href="survey">설문조사</a> </li>
{% if 'user_id' in request.session %}
<li>{{request.session.user_name}}님 반갑습니다. </li>
<li><a href="logout">로그아웃</a> </li>
{% else %}
<li><a href="/web1/tripmember">회원가입</a> </li>
<li><a href="loginform">로그인폼</a> </li>
{% endif %}
</ul>

 

[ 실습 예제 ]

[ cx_oracle과 연동해서 로그인 프로그램 완성하기 ]

def getLoginChk() 함수를 만들어서 인자 정의 및 구현을 한다.

 

13. 로그인을 체크하는 모델 추가(models.py)

오라클과 연결하고 sql문을 전송 및 값을 받아온다. datas의 경우 fetchall()로 모든 결과를 리턴한다.

[ Fatch 종류 ]

fetchall()의 경우 결과를 모두 리턴
fetchone()의 경우 하나의 row를 리턴
fetchmany(num rows)의 경우 rows의 숫자 만큼 리턴을 한다.

 


  
from django.db import models
import cx_Oracle as ora
database = 'kosmorpa/test00@192.168.0.122:1522/orcl'
# Create your models here.
# select count(*) cnt, name from member_table where id='xman' and pwd='121'
# 인자값이 여러개일 경우 함수의 인자를 늘여서 사용하게 되는 부분을 리팩토링한다.
def getLoginChk(**kwargs):
conn = ora.connect(database)
cursor = conn.cursor()
sql_select = "select count(*) cnt, name from member_table" \
" where id=:id and pwd=:pwd group by name"
cursor.execute(sql_select, id=kwargs['id'], pwd=kwargs['pwd'])
datas = cursor.fetchall()
cursor.close()
conn.close()
return datas

 

 

▼kwargs와 args

더보기

kwargs와 args는 파이썬에서 기존의 함수에 파라미터를 추가하여 새로운 함수를 만드는 경우에 사용한다.

별표시(***)의 경우, 여러 개의 인수를 받을 때, 키워드 인수를 받을 때 사용하는 표시이다.

 

1) **kwargs (keyword argument)

 

**kwargs는 {'키워드': '특정 값'} 형태로 함수를 호출할 수 있다. 또는 특정 키워드에 반응하여 함수를 작성하는 방법도 있다.(if문 사용) 단, 신규로 추가할 파라미터를 **kwargs 의 왼쪽에 써야 한다.

ex)def a(**kwargs, a): 은 틀린 문법(SyntaxError)

 

2) *args(argument)

 

*args 여러개의 인자를 함수로 받고자 할 때 쓰인다. 이름은 임시로 지은거라 *a , *banana 등등 다 가능하다. 튜플(tuple) 형태로 여러개의 인자를 함수로 출력하게 될 경우 {'a', 'b', 'c' } 와 같이 튜플 형태로 출력된다.

 

(예시 출처)

 

1. hello( ) 함수가 있습니다. 부서명과 이름을 받아 인사합니다.

 

2. 어느날 여러 번 인사와 함께 just do it! 을 외치는 새로운 함수를 만들어야 합니다.

단순히 아래처럼 같은 내용을 또 선언 할 수도 있습니다.

)

런 경우 hello( ) 내용이 달라지면 hello_repeat도 변경해야하는 번거로움이 있습니다.

 

3. 이런 경우 기존 hello( )를 그대로 사용하면서 파라미터만 추가로 선언하는 방법을 사용할 수 있습니다.

결과가 같습니다. 이제 hello( ) 내용이 변경되더라도 hello_repeat( )는 변경할 필요가 없습니다.

kwargs와 args는 파이썬에서 기존의 함수에 파라미터를 추가하여 새로운 함수를 만드는 경우에 사용한다.

별표시(***)의 경우, 여러 개의 인수를 받을 때, 키워드 인수를 받을 때 사용하는 표시이다.

 

1) **kwargs (keyword argument)

 

**kwargs는 {'키워드': '특정 값'} 형태로 함수를 호출할 수 있다. 또는 특정 키워드에 반응하여 함수를 작성하는 방법도 있다.(if문 사용) 단, 신규로 추가할 파라미터를 **kwargs 의 왼쪽에 써야 한다.

ex)def a(**kwargs, a): 은 틀린 문법(SyntaxError)

 

2) *args(argument)

 

*args 여러개의 인자를 함수로 받고자 할 때 쓰인다. 이름은 임시로 지은거라 *a , *banana 등등 다 가능하다. 튜플(tuple) 형태로 여러개의 인자를 함수로 출력하게 될 경우 {'a', 'b', 'c' } 와 같이 튜플 형태로 출력된다.

 


(예시 출처)

 

1. hello( ) 함수가 있습니다. 부서명과 이름을 받아 인사합니다.


  
def hello(department, name):
print('hello ' + department + ' team ' + name)

 

2. 어느날 여러 번 인사와 함께 just do it! 을 외치는 새로운 함수를 만들어야 합니다.

단순히 아래처럼 같은 내용을 또 선언 할 수도 있습니다.


  
def hello_repeat(num, department, name):
for i in range(num):
print('hello ' + department + ' team ' + name)
print('just do it!')

)

런 경우 hello( ) 내용이 달라지면 hello_repeat도 변경해야하는 번거로움이 있습니다.

 

3. 이런 경우 기존 hello( )를 그대로 사용하면서 파라미터만 추가로 선언하는 방법을 사용할 수 있습니다.


  
def hello_repeat(num, **kwargs):
for i in range(num):
hello(**kwargs)
print('just do it!')

결과가 같습니다. 이제 hello( ) 내용이 변경되더라도 hello_repeat( )는 변경할 필요가 없습니다.


 

 

15. views 가서 바꾸기

DB를 연결했으므로 임의로 정한 db_id, db_pwd가 아닌 오라클 DB에 있는 id와 pwd인지 if문을 이용해서 결과값을 리턴한다. 세션에 저장된 user_id가 id와 같을 경우, 로그인 성공창과 함께 0행 1열(2차원 배열 : 즉, 1행2열)을 이름값으로 가져온다.로그인이 실패되면 msg를 띄운다.


  
@csrf_protect
def loginform(request):
# 세션에 등록한 값이 존재하는가?
if 'user_id' in request.session:
return redirect('/login')
if request.method == 'POST':
user_id = request.POST['id']
user_pwd = request.POST['pwd']
print('user_id: ', user_id)
print('user_pwd: ', user_pwd)
# ----------------- 리펙토링한거 ------------------
res = getLoginChk(id=user_id,pwd=user_pwd)
print('='*30)
print(res)
if len(res) > 0:
print('로그인 성공')
request.session['user_id'] = user_id
request.session['user_name'] = res[0][1]
return redirect('/login')
else:
print('로그인 실패')
msg = '아이디나 비밀번호가 잘못되었습니다.'
return render(request, "login/login.html", {"error": msg})
return render(request, "login/login.html")

 

좌 : 기존 우 : 바꾼거

DB와 연동되어 로그인이 잘 되었다.

 

로그인 성공
로그인에 실패했을 경우

▼ 전체 코드 보기

더보기

[전체 코드]

1. index.html


  
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>index.html</title>
<style>
ol, ul {list-style:none;}
ul li { float:left; margin-left:2px; background:pink}
</style>
</head>
<body>
<h1>로그인/로그아웃 데모</h1>
<span>추후 web1에 템플릿 디자인으로 적용하세요.</span>
<nav id="menu">
<header class="major">
<ul>
<li><a href="main">Home</a> </li>
<li><a href="survey">설문조사</a> </li>
{% if 'user_id' in request.session %}
<li>{{request.session.user_name}}님 반갑습니다. </li>
<li><a href="logout">로그아웃</a> </li>
{% else %}
<li><a href="/web1/tripmember">회원가입</a> </li>
<li><a href="loginform">로그인폼</a> </li>
{% endif %}
</ul>
</header>
</nav>
</body>
</html>

2. login.html


  
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>login.html</title>
<style>
ol, ul {list-style:none;}
ul li { float:left; margin-left:2px; background:pink}
</style>
</head>
<body>
<h1>로그인 페이지</h1>
<span>추후 web1에 템플릿 디자인으로 적용하세요.</span>
<nav id="menu">
<section id="main">
<header>
<h2>StarTrip</h2>
<form method="post" action="">
{% csrf_token %}
<div class="fields">
<div class="field">
<input type="text" name="id" id="id" value="" placeholder="ID"
style="text-transform :lowercase"/>
</div>
<div class="field">
<input type="password" name="pwd" id="pwd" placeholder="Password" />
</div>
</div>
<ul class="actions special">
<li><input type="submit" value="로그인" class="primary"/></li>
</ul>
<div><span style="color:red;">{{error}}</span> </div>
</form>
</header>
</section>
</nav>
</body>
</html>

3. models.py


  
from django.db import models
import cx_Oracle as ora
database = 'kosmorpa/test00@192.168.0.122:1522/orcl'
# Create your models here.
# select count(*) cnt, name from member_table where id='xman' and pwd='121'
# 인자값이 여러개일 경우 함수의 인자를 늘여서 사용하게 되는 부분을 리팩토링한다.
def getLoginChk(**kwargs):
conn = ora.connect(database)
cursor = conn.cursor()
sql_select = "select count(*) cnt, name from member_table" \
" where id=:id and pwd=:pwd group by name"
cursor.execute(sql_select, id=kwargs['id'], pwd=kwargs['pwd'])
datas = cursor.fetchall()
cursor.close()
conn.close()
return datas

4. urls.py


  
from django.urls import path
from login import views
urlpatterns=[
path('',views.home),
path('loginform',views.loginform),
path('logout',views.logout),
]

5. views.py


  
from django.shortcuts import render, redirect
from .models import *
# Create your views here.
from django.views.decorators.csrf import csrf_protect
def home(request):
return render(request, "login/index.html")
# 1. 전송방식 구분
# 2. Session 유무에 따른 구분
@csrf_protect
def loginform(request):
# 세션에 등록한 값이 존재하는가?
if 'user_id' in request.session:
return redirect('/login')
if request.method == 'POST':
user_id = request.POST['id']
user_pwd = request.POST['pwd']
print('user_id: ', user_id)
print('user_pwd: ', user_pwd)
# ----------------- 리펙토링한거 ------------------
res = getLoginChk(id=user_id,pwd=user_pwd)
print('='*30)
print(res)
if len(res) > 0:
print('로그인 성공')
request.session['user_id'] = user_id
request.session['user_name'] = res[0][1]
return redirect('/login')
else:
print('로그인 실패')
msg = '아이디나 비밀번호가 잘못되었습니다.'
return render(request, "login/login.html", {"error": msg})
return render(request, "login/login.html")
# from .models import * 위쪽에 해줘야함
# ---------------------기존 코드 -> 리펙토링 해보기 ------------------------------
# db_id = 'xman'
# db_pwd = '11'
# if user_id in db_id and user_pwd in db_pwd:
# print('로그인 성공')
# request.session['user_id'] = db_id
# request.session['user_name'] = '김길동'
# return redirect('/login')
# else:
# print('로그인 실패')
# msg = '아이디나 비밀번호가 잘못되었습니다.'
# return render(request,"login/login.html",{"error":msg})
# return render(request, "login/login.html")
# ------------------------------------------------------------------------------
# 로그아웃 처리
def logout(request):
del request.session['user_id']
del request.session['user_name']
return redirect('/login')

 

cf) 웹브라우저에 SessionId가 쿠키에 저장되어있는지 확인하는 방법

 

반응형
LIST
반응형
SMALL

+ Recent posts

반응형
LIST