스프링 기초 DI 예제 4


[스프링 컨테이너 생명 주기]

      

      스프링 컨테이너 생성   : GenericXmlApplicationContext ctx = new GenericXmlApplicationContext();

       ------> 설정    :  ctx.load("classpath:baseBall.xml");

                     ctx.refresh(); 

       ------> 사용    :  Player player =ctx.getBean("player".Player.class); 

            player.getName();

            player.getPosition();

       ------> 종료    :ctx.close()



 [빈 Life cycle(생명주기)]

   

   ctx.refresh 과정에서 빈이 생성 

   

   ctx.close() 컨테이너가 소멸하는 단계에서 빈도 자동으로 소멸됩니다.

    

   InitializingBean 인터페이스 -  초기화 할때 작동

    구현해야 할 메소드

    public void afterPropertiesSet() throws Exception

   

   DisposableBean 인터페이스 -  소멸될때 작동

    public void destroy() throws Exception  

   InitializingBean, DisposableBean은 따로 구현 해도 되고, 동시에 구현해도 된다.


# 어노테이션을 활용하는 방법

    @PostConstruct

    public void initMethod(){}

     - 빈 초기화 과정에서 빈이 생성되기 전에 해야할 작업을 할 수 있다.

     

     

    @PreDestroy    

public void destroyMethod(){}

- 종료되는 시점에 빈 소멸전에 해야할 작업을 할 수 있다.

(컨테이너 안에 빈이 들어 있다.)


예제1)





   [빈의 범위(scope)]

범위는 해당하는 객체가 어디까지 영향을 미치는지 결정하는 것

<bean> 태그의 속성 값 : default 값은 singleton

  singleton : 스프링 컨테이너에 의해 한개의 빈객체만 생성

  prototype : 빈을 사용할 때 마다 객체를 생성

  request : HTTP 요청 마다 빈 객체를 생성(WebApplicationContext에서만 적용)

  session : HTTP 세션 마다 빈 객체를 생성(WebApplicationContext에서만 적용)

  global-session : 글로발 HTTP 세션에 대해 빈 객체를 생성. 





[커스텀 초기화 메소드/ 커스텀 소멸 메소드]  

:객체의 생성과 소멸시에 특정 메소드를 실행시키고 싶을 때 사용한다.  

- init-method : 커스텀 초기화 메서드를 지정하는 <bean>태그의 속성

- destroy-method : 커스텀 소멸 메서드를 지정하는 <bean>태그의 속성 

** BeanNameAware 인터페이스

: 빈객체가 자기자신의 이름을 알아야 할 경우 사용된다.

: 클래스가 BeanNameAware 인터페이스를 구현한 경우 컨테이너는 setBeanName()메서드를 호출해서 빈객체의 이름을 전달

: setBeanName(String arg) : 객체가 생성될 때 해당 객체의 id나 name값을 전달 받는 메서드



[외부 파일을 이용한 빈 설정]

 

- EnvironmentAware, Envirionment 인터페이스를 활용

 

  . Context  --> getEnvironment() -->Environment 객체 얻기

  . Environment  -->getPropertySources 객체를 얻기

  . PropertySources -->프로퍼티를 추가(addLast)하거나, 추출(getProperty) 작업을 한다.

 

 

- 프로퍼티 파일을 이용한 빈설정

 

  .XML 파일에 외부프로퍼티 파일을 명시하는 방법

 

 

  .Java 파일에 외부 프로퍼티 파일을 명시하는 방법

 

 










springDemo_2.zip


'매일코딩 > Spring' 카테고리의 다른 글

스프링 기초 DI 예제 4  (0) 2018.07.17
스프링 기초 DI 예제 3  (0) 2018.07.17
스프링 기초 DI 예제2  (0) 2018.07.16
스프링 기초 DI 예제1  (0) 2018.07.16
31.스프링프로젝트 - 조회  (0) 2016.11.21
30.스프링프로젝트 - 답변  (0) 2016.11.21

스프링 기초 DI 예제 3



[의존성 주입 종류]

    setter(설정 메소드)를 이용한 주입 

    : 설정 메소드를 사용해서 의존성을 주입하는 것

    

    Constructor(생성자)를 통한 주입

    : 생성자를 사용해서 의존성을 주입하는 것

   

   

   [의존성 주입(DI)의 장점]

DI를 사용하면 개발 계획시에 시간이 요구가 되지만, 규모가 큰 프로젝트에서 유지보수 업무를 

한다면 DI 개발의 장점을 느낄 수 있다.  

   

   

   [의존 관계 설정 방법]

   - XML파일을 이용한 설정방법

   

   - JAVA를 이용한 설정방법(어노테이션을 이용한다. @Configuration, @Bean)

     

     : 어노테이션(Annotation:Metadata)-JDK5부터 등장한 개념 ex)@Override

         선언시에는 @를 사용하여 선언한다.

         어노테이션은

     . 컴파일러에게 정보를 알려주거나

     . 컴파일 할 때와 설치(deployment)시의 작업을 지정하거나

     . 실행할 때 별도의 처리가 필요한 경우

         사용한다.

   클래스, 메소드, 변수 등 모든 요소에 선언이 가능하다.

   

       

       # @Configuration : 클래스앞에 선언하는데, "이 클래스는 스프링 설정에 사용되는 클래스 입니다" 라고 알려주는 어노테이션

       # @Bean : 메소드 앞에 선언, "객체를 생성"

   


예제1)


예제2) 자바로 빈객체 만들기

예제3)



예제4)자바 코드에서 xml 불러오는 방법


springDemo_1.zip


 

'매일코딩 > Spring' 카테고리의 다른 글

스프링 기초 DI 예제 4  (0) 2018.07.17
스프링 기초 DI 예제 3  (0) 2018.07.17
스프링 기초 DI 예제2  (0) 2018.07.16
스프링 기초 DI 예제1  (0) 2018.07.16
31.스프링프로젝트 - 조회  (0) 2016.11.21
30.스프링프로젝트 - 답변  (0) 2016.11.21


sql 인젝션



1.입력데이터 검증 기법

2.sql 인젝션

3.명령어 인젝션 취약점원인



입력데이터 검증 방식 


입력데이터 처리를 위한 규칙




설계자나 개발자는 외부에서 입력되는 값을 철저히 통제해야됨



입력값 검증은 애플리케이션에서 먼저 체크되어야 함


서버 애플리케이션에도 체크되어야 함



view  컴포넌트, controller 컴포넌트에 동시에 체크 해야 함



#sql 인젝션 취약점 개요



웹 애플리케이션에서 값을 입력받을 때 

동적 쿼리 생성, 데이터 베이스에 쿼리를 전달하여 실행하는 경우 발생


검증되지 않은 외부 입력값으로 인해 정상적인 쿼리가 변조되어 데이터 베이스에 불법적으로

데이터 열람, 수정, 삭제 됨


저장된 프로시저를 사용하여 시스템 명령 수행




비정상적으로 데이터베이스에 접근하거나 제어하는 공격 기법


모든 종류의 dbms에 적용 가능한 공격 기법




예) 악성 스크립트의 실행, 외부 프로그램의 사용, 불법 로그인, db 정보 열람, 추가,수정,삭제


프로시저를 통한 운영체제 명령어 수행



#sql 취약점 진단



form based sql 인젝션 진단



정적 쿼리 구조를 이용하여 sql 실행




명령어 인젝션 취약점 개요























































보안이슈 및 개발보안의 필요성1



취약한 애플리케이션 원인 70% - 80%


분석 => 설계 =>  개발 => 테스트 =>  배포 =>  운영


sql 인젝션 취약점


xss 취약점(크로스 사이트 스크립팅) - 이메일 본문에 스크립트 명령어를 삽입하여

본문을 열람하기만 하여도 악성 코드에 감염 될 수 있다.


시큐어 코딩 적용하면 94% 정도 해킹 감소


웹 애플리케이션 보안의 필요성 


침해사고 사례를 통해 시큐어 코딩의 필요성 설명 


소프트웨어 보안 약점과 취약점 정보 활용가능


행정자치부의 소프트웨어 개발보안 가이드를 활용가능


소프트웨어 개발보안 방법론을 설명 할 수 있음



보안 솔류션 


종류


한계




방화벽이나 ids /ips 같은 보안 장비를 구축해서


원격으로 시스템의 취약한 서비스를 이용한 공격은 사실상 어려움



해커들은 웹 포트 80번을 집중적으로 공략



안전한 웹보안을 보장하기 쉽지 않음 -  기업마다 서비스 환경이 다르기 때문



웹 기반 공격 유형


1차 해킹 - 외부망 통한 해킹   > 웹서버의 취약점을 공격하는 방법과 내부 pc를 공격하는 방법이 있음


웹서버 해킹은 웹사이트를 통해 이루어지며 sql 인젝션이나 파일 업로드 취약점과 같은 웹 애플리케이션의 취약점 공격을 시도 하고

관리자 계정을 확보해서 내부시스템으로 침투할 수 있는 발판을 만드는 방식


2차 해킹 - 내부망 통한 해킹 -> 내부사용자의 실수로 웹사이트나 메일을 통해 악성코드가 설치되도록 하는 것




보안 종류 - 네트워크 보안, 클라이언트 보안, 애플리케이션 보안, 데이터베이스 보안


- 네트워크 보안 


- 시스템 보안 


- 애플리케이션 보안



 kisa 분석결과 네트워크 보안 장비인 방화벽이나 ids/ips는 정의된 룰이나

시그니처를 이용하여 안전하지 않은 패킷을 필터링하는 정책을 사용한다.

하지만 포트 스캔, dos, 잘못관리된 룰셋, 새로운 패턴의 공격으로 3.5%의 침해사고가 발생하고 있다.

웹 방화벽도 공격에 사용된 데이터의 시그니쳐를 룰로 정의 하여 해당 패턴의 요청의 패턴을 차단하게 되는데

잘 알려진 애플리케이션 레벨의 공격은 잘 차단하고 있지만, 새로운 공격 패턴이나 룰 관리 부실로 인한 침해 사고율이 10% 정도 된다.



악성코드 유입량의 지속적인 증가. 예) 이메일


침해사고 사례














보안약점 vs 보안 취약점



보안약점 : 소프트웨어에서 해킹등 실제 보안사고에 악용될 수 있는 보안 취약점의 근본원인


보안취약점: 소프트웨어 실행 시점에 여러개의 보안 약점중 실제 침해사고의 원인이 되는 그 보안 약점


예방책: 시큐어 코팅


25가지 보안약점 정리 한것 : CVE 목록 - os, 매년 8천여개 목록이 추가됨. (발견된 보안 취약점의 history)



cwe : 소프트웨어의 보안 약점을 사전식으로 관리 하고 미국토안보부에서 관리한다.


수집된 보안약점을 뷰 카테코리 취약점 복합요소 별로 볼 수 있다.


일반적인 취약점의 분류체계



owasp top 10 : 운영중인 웹서버에 발생되는 가장 많은 침해 사고 유형을 정리한 리스트 


애플리케이션 보안을 전문으로 하는 7개 기업의 8개 데이터 세트를 토대로 함.


취약점 공격 가능성, 탐지 가능성, 영향평가 등을 고려하여 가장 많이 퍼져 있고,

위험도가 큰 상위 10개를 선정했다.

리스트의 순서가 중요도 , 빈도와 심각성을 반영하고 있어서 개발자들이 쉽게 위험도를 확인 할 수 있다.


소프트웨어 개발 보안 가이드 주요 내용


입력 데이터 검증 및 표현


보안기능


시간 및 상태

에러 처리


코드오류


api 오용











































스프링 기초 DI 예제2


코드의 변경 없이 xml 설정만으로 개발자가 원하는 객체의 주입으로 바꿀 있음.

   

   

   [의존성 주입 종류]

    setter(설정 메소드) 이용한 주입 

    : 설정 메소드를 사용해서 의존성을 주입하는

    

    Constructor(생성자) 통한 주입

    : 생성자를 사용해서 의존성을 주입하는

   

   

   [의존성 주입(DI) 장점]

DI 사용하면 개발 계획시에 시간이 요구가 되지만, 규모가 프로젝트에서 유지보수 업무를 

한다면 DI 개발의 장점을 느낄 있다.  




예제1)















예제2)





















'매일코딩 > Spring' 카테고리의 다른 글

스프링 기초 DI 예제 4  (0) 2018.07.17
스프링 기초 DI 예제 3  (0) 2018.07.17
스프링 기초 DI 예제2  (0) 2018.07.16
스프링 기초 DI 예제1  (0) 2018.07.16
31.스프링프로젝트 - 조회  (0) 2016.11.21
30.스프링프로젝트 - 답변  (0) 2016.11.21

스프링 기초 DI 예제1



스프링 프레임워크 특징 1


* 먼저 스프링 프레임워크는 자바언어를 기반으로 한 어플리케이션을 만들때 효율적으로 빠르게 제작할 수 있도록 하는 프로그래밍 툴이다.

* 스프링은 자바의 객체를 담고 있는 컨테이너다.

* 객체의 생성, 소멸과 같은 생명주기를 관리한다.

* DI(의존성 주입패턴)을 지원한다.

* AOP, 트랜잭션이나, 로깅, 보안과 같은 엔터프라이즈 어플리케이션에서 공통으로 필요로 하는 기능을 분리해서 각모듈에 적용할 수 있도록 하는 기능을 제공한다.

* 스프링은 POJO를 지원한다. 즉 특정 인터페이스나 클래스를 상속받지 않는 순수한 자바 객체를 스프링 컨테이너가 저장하고 있다.

* 트랜잭션 처리를 위한 일관된 방식을 제공한다.

* 영속성과 관련된 다양한 API를 제공한다.

* Mybtis, JPA, JDBC 등과 같은 프레임워크와의 연동을 지원한다.


DI(의존성주입)

DI 스프링의 핵심 개념 중의 하나이다.

객체 사이의 의존 관계를 객체 자신이 아닌 외부(스프링 컨테이너)에서 수행하는 개념

* 의존관계의 설정은 설정파일(bean.xml)이나 어노테이션을 이용하여 설정 한다.


예1)

프로젝트 생성


test.xml에 bean을 통해서 TestDAOImp 객체를 만들었다.




인터페이스 메소드 구현


인터페이스




예2) DI 예제



bean으로 객체 생성 -> myGetSum이 GetSum 참조 -> property name 을 이용해서 MyGetSum setter 함수에 값을 할당 했다.







예3) 생성자를 통해서 값 주입하기




예4)















'매일코딩 > Spring' 카테고리의 다른 글

스프링 기초 DI 예제 3  (0) 2018.07.17
스프링 기초 DI 예제2  (0) 2018.07.16
스프링 기초 DI 예제1  (0) 2018.07.16
31.스프링프로젝트 - 조회  (0) 2016.11.21
30.스프링프로젝트 - 답변  (0) 2016.11.21
29.spring- detgul  (0) 2016.11.18

[node js] 성경 db 연동

mysql 성경 db를 찾아서 데이터를 넣어준 이후 작업.


bible.js

var express = require('express')
var app = express()
var router = express.Router()
var mysql = require('mysql')
var fs = require('fs')
var ejs = require('ejs')




//성경 메인 화면
router.get("/bible",function(req,res){
console.log("메인화면 진행")
//console.log(searchBibleIndex("여호수아"))

fs.readFile('bible.html', 'utf-8', function (error, data) {
res.send(ejs.render(data, {
data: [],
title: {book:"",chapter:""}
}));
});
})


//메인 화면
router.post("/bible_search",function(req,res){
console.log("바이블 찾기 포스트 진행")
var maintext = req.body.maintext

//창세기이면 01, 출애굽기

maintext = searchBibleIndex(maintext)

var jang = req.body.jang
var jeol = req.body.jeol
var jeol2 = req.body.jeol2
console.log(maintext,jang,jeol,jeol2)

var queryString = 'select book, chapter,verse,content from bible_korHRV where book = ? and chapter = ? and verse between ? and ?'
fs.readFile('bible.html', 'utf-8', function (error, data) {
getConnection().query(queryString, [maintext,jang,jeol,jeol2], function (error, result) {
if (error) {
console.log("바이블 데이터 로드 에러" + error);
return
}
//var bookName = searchBibleName(result[0].book)

res.json(result)

// res.send(ejs.render(data, {
// data: result,
// bookname:bookName,
// chapter : result[0].chapter
// }))
})
});

})


function searchBibleName(index) {

var searchIndex = index - 1

var book= [ "창세기",
"출애굽기",
"레위기",
"민수기",
"신명기",
"여호수아",
"사사기",
"룻기",
"사무엘상",
"사무엘하",
"열왕기상",
"열왕기하",
"역대상",
"역대하",
"에스라",
"느헤미야",
"에스더",
"욥기",
"시편",
"잠언",
"전도서",
"아가서",
"이사야",
"예레미야",
"예레미야애가",
"에스겔",
"다니엘",
"호세아",
"요엘",
"아모스",
"오바댜",
"요나",
"미가",
"나훔",
"하박국",
"스바냐",
"학개",
"스가랴",
"말라기",
"마태복음",
"마가복음",
"누가복음",
"요한복음",
"사도행전",
"로마서",
"고린도전서",
"고린도후서",
"갈라디아서",
"에베소서",
"빌립보서",
"골로새서",
"데살로니가전서",
"데살로니가후서",
"디모데전서",
"디모데후서",
"디도서",
"빌레몬서",
"히브리서",
"야고보서",
"베드로전서",
"베드로후서",
"요한일서",
"요한이서",
"요한삼서",
"유다서",
"요한계시록"]


return book[searchIndex]
}

function searchBibleIndex(name) {

var book= [ "창세기",
"출애굽기",
"레위기",
"민수기",
"신명기",
"여호수아",
"사사기",
"룻기",
"사무엘상",
"사무엘하",
"열왕기상",
"열왕기하",
"역대상",
"역대하",
"에스라",
"느헤미야",
"에스더",
"욥기",
"시편",
"잠언",
"전도서",
"아가서",
"이사야",
"예레미야",
"예레미야애가",
"에스겔",
"다니엘",
"호세아",
"요엘",
"아모스",
"오바댜",
"요나",
"미가",
"나훔",
"하박국",
"스바냐",
"학개",
"스가랴",
"말라기",
"마태복음",
"마가복음",
"누가복음",
"요한복음",
"사도행전",
"로마서",
"고린도전서",
"고린도후서",
"갈라디아서",
"에베소서",
"빌립보서",
"골로새서",
"데살로니가전서",
"데살로니가후서",
"디모데전서",
"디모데후서",
"디도서",
"빌레몬서",
"히브리서",
"야고보서",
"베드로전서",
"베드로후서",
"요한일서",
"요한이서",
"요한삼서",
"유다서",
"요한계시록"]
var changeBookList =[];

for(var i = 1; i < 67; i++){
var bookChange = { value :book[i-1]}
changeBookList.push(bookChange)
bookChange = {}
}

var bookIndex = changeBookList.map(function (book) { return book.value; }).indexOf(name);

console.log(bookIndex);
console.log(bookIndex+1);

if(bookIndex === -1){
return "데이터를 다시 확인해주세요"
}else{
return bookIndex+1
}
}



var pool = mysql.createPool({
connectionLimit: 10,
host: 'localhost',
user: 'root',
database: 'wow',
password: '1111'
})


//디비 연결 함수
function getConnection() {
return pool
}


module.exports = router


bible.html

<!DOCTYPE html>
<html lang="en">
<head>
<title>성경 검색</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
</head>
<body>

<div class="jumbotron text-center">
<h1>성경검색</h1>
<p>찾을구절을 검색하세요</p>
</div>


<div class="container">
<h2>성경본문찾기</h2>
<p>찾으실 성경 본문을 입력해보세요</p>
<form class="form-inline" action="/bible_search" method="POST">
<div class="form-group">
<label for="maintext">성경본문</label>
<input type="text" class="form-control" id="maintext" placeholder="창세기" name="maintext">
</div>
<br><br><br>
<div class="form-group">
<input type="text" class="form-control" id="jang" placeholder="1장" name="jang">
<label for="jang"></label>
</div>
<div class="form-group">
<input type="text" class="form-control" id="jeol" placeholder="1절" name="jeol">
<label for="jeol"></label>
</div>
<br>
<br>
<h4>에서부터</h4>
<br>
<br>

<div class="form-group">
<input type="text" class="form-control" id="jeol2" placeholder="10절" name="jeol2">
<label for="jeol2"></label>
</div>
<br>
<br>
<h4>까지</h4>
<br>
<br>
<button type="submit" class="btn btn-default">찾기</button>
</form>
</div>

<br>
<br>

<div class="container">
<% var bookname = bookname %>
<% var chapter = chapter %>
<%
if(bookname && chapter){
%>
<h2><%= bookname %> 본문</h2>
<h3><%= chapter %> 장</h3>

<% }%>
<table class="table table-striped">
<thead>
<tr>
<th>구절</th>
<th>내용</th>
</tr>
</thead>
<tbody>

<% data.forEach(function (item,index){ %>
<tr>
<td>
<%= item.verse %>
</td>
<td>
<%= item.content %>
</td>
</tr>

<% })%>
</tbody>
</table>
</div>
</body>
</html>


결과화면




우분투 mysql에 외부에서 접속하기 


가비아에서 클라우드 서버를 쓰고 있는데 ip/phpmyadmin 들어가서 쿼리 작업하기가 많이 불편했다. 이참에 나의 맥북으로 원격으로 접속할 수 있게 설정을 해보았다. 사진위주로 정리해보았다.


1.나의 맥북 터미널로 ssh 서버 원격 접속!

2. etc/mysql/ 위치로 가서 vi my.cnf 열기~!

3.bind-address 부분 주석처리 #bind-address ! 이렇게!

4.sudo service mysql restart - 재시작!




5.  mysql -uroot -p 접속

6.select user, host from mysql.user  (유저와 호스트 확인)

7. % 이렇게 퍼센트로 기입된 host가 모든 host 허용이라는 뜻(나는 이미해놨음)

8.사진에 create ~~ 명령문 입력

9.명령문 다 입력후 flush privileges 를 꼭 적어준다.!!





아래는 3306 포트 열어주는 명령어 그리고 mysql 재시작





그리고 가비아 서버 매니저로 가서 방화벽정책에 mysql 3306 포트를 허용해줘야 한다. 

이것때문에 30분정도...삽질.




mysql워크벤치 확인~!




[node js] 파일전송


네비게이션



file.js

var express = require('express')
var app = express()
var router = express.Router()
var mysql = require('mysql')
var fs = require('fs')
var ejs = require('ejs')

//파일관련 모듈
var multer = require('multer')

//파일 저장위치와 파일이름 설정
var storage = multer.diskStorage({
destination: function (req, file, cb) {
//파일이 이미지 파일이면
if (file.mimetype == "image/jpeg" || file.mimetype == "image/jpg" || file.mimetype == "image/png") {
console.log("이미지 파일이네요")
cb(null, 'uploads/images')
//텍스트 파일이면
} else if (file.mimetype == "application/pdf" || file.mimetype == "application/txt" || file.mimetype == "application/octet-stream") {
console.log("텍스트 파일이네요")
cb(null, 'uploads/texts')
}
},
//파일이름 설정
filename: function (req, file, cb) {
cb(null, Date.now() + "-" + file.originalname)
}

})
//파일 업로드 모듈
var upload = multer({ storage: storage })


//파일 업로드 및 디비에 위치 저장
router.post('/upload_images', upload.single('fileupload'), function (req, res) {
console.log("post")
console.log(req.file)
console.log(req.file.path)
console.log(upload)
console.log(upload.storage.getFilename)

//파일 위치를 mysql 서버에 저장
getConnection().query('insert into myfile(name) values (?)', [req.file.path], function () {

res.redirect('/filepage');
});
});



//파일 페이지 보여주기
router.get("/filepage", function (req, res) {
console.log("파일 페이지 나와라")

//파일 가져올 위치
var path = __dirname + '/../' + 'uploads/images/'

fs.readFile('file.html', 'utf-8', function (error, data) {
var queryString = 'select * from myfile'
getConnection().query(queryString, function (error, result) {
if (error) {
console.log("파일가져올때 에러 발생" + error);
return
}
res.send(ejs.render(data, {
data: result
}));
});
})


})

//파일 다운로드 눌렀을 때 작동
router.get('/download/uploads/images/:name', function (req, res) {
var filename = req.params.name;

var file = __dirname + '/../uploads/images/' + filename
console.log(__dirname)
console.log(req.path)
console.log(file)
res.download(file); // Set disposition and send it.
});


var pool = mysql.createPool({
connectionLimit: 10,
host: 'localhost',
user: 'root',
database: 'wow',
password: '1111'
})


//디비 연결 함수
function getConnection() {
return pool
}


module.exports = router


file.html

<!DOCTYPE html>
<html lang="en">
<head>
<title>파일</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
</head>
<body>

<div class="container">
<div class="jumbotron">
<h1>파일업로드</h1>
<p> </p>
</div>
<p>.</p>
<a href="/main">게시판으로 이동</a>
</div>


<br>
<br>
<div class="container">
<form action="/upload_images" method="post" enctype="multipart/form-data">
<div class="form-group">
<label for="exampleFormControlFile1">파일업로드</label>
<input type="file" class="form-control-file" id="fileupload" name="fileupload">
</div>
<input type="submit" value="올리기">
</form>
</div>

<br>
<br>
<br>
<h2>파일 다운로드</h2>
<ul>
<% data.forEach(function (item,index){ %>

<li><a href='/download/<%= item.name%>'><%= item.name%></a></li>

<% })%>
</ul>
</body>
</html>


결과화면


3년전인가 처음 게시판을 만들어보고 거의 처음인것 같다. 언제쯤이면 머릿속에 생각했던 내용들을 거침없이 만들 수 있는 날이 올까나. 암튼 게시판은 스스로 안보고 짤 수 있을 때까지 연습해보면 실력향상에 도움이 많이 되는 미니프로젝트긴하다. 나중에 게시판에 좀더 기능을 넣을 수도 있다. 예를 들면 db설계를 좀 해서 댓글 기능, 좋아요 기능, 조회기능 등을 넣어서 게시판을 확장 시킬 수 있다. 이번 포스팅에서는 아주 아주 심플한 게시판을 만들어서 페이징 기능의 핵심만 정리해본다.

네비게이션

아래는 파일 네비게이션이다. 우리가 사용할 파일들은 빨간점으로 표시해놓았다.


node js  기본셋팅(app.js)

app.js를 아래와 같이 작성한다. 게시판이랑 상관없는 로직도 있지만 지우지 않았다... var productRouter 부분 부터 자세히 보자. 우리가 만들 게시판과 직접 관련된 로직이다. 웹브라우저에 /main, /board 라고 적으면 ./routes/product.js에서 처리하게 해놓았다.그럼 products.js 페이지 부분으로 가보자

//모듈 임포트
var express = require('express');
var app = express()
var morgan = require('morgan') //로그 모듈 임포트
var mysql = require('mysql') //mysql 임포트
var bodyParser = require('body-parser');
//미들웨어 설정
app.use(morgan('short')) //로그 미들웨어
app.use(express.static('./public')) //기본 파일 폴더 위치 설정
app.use(bodyParser.urlencoded({extended:false}))
//라우트로 분리시켜주기
var userRouter = require('./routes/user.js')

//상품리스트 게시판~!
var productRouter = require('./routes/product.js')
app.use(userRouter)
app.use(productRouter)
//var PORT = process.env.PORT || 3003
//서버 가동
app.listen(3003,function(){
console.log("서버가동")
})


products.js (라우터)

가장 중요한 부분. 웹브라우저에서 넘어오는 url을 전부 이곳에서 받는다. 유심히 보아야 할 부분이 /main 부분과 /pasing/"cur 부분이다. 가장 처음으로 들어온 유저는 가장 첫페이지를 보여줘야 하기 때문에 기본값 1을 페이징 함수에 넘겼다. 나머지 설명은 주석에 적어 놓았다.

var express = require('express')
var router = express.Router()
var mysql = require('mysql')
var fs = require('fs')
var ejs = require('ejs')
var bodyParser = require('body-parser');


router.use(bodyParser.urlencoded({ extended: false }))


//게시판 페이징

router.get("/pasing/:cur", function (req, res) {

//페이지당 게시물 수 : 한 페이지 당 10개 게시물
var page_size = 10;
//페이지의 갯수 : 1 ~ 10개 페이지
var page_list_size = 10;
//limit 변수
var no = "";
//전체 게시물의 숫자
var totalPageCount = 0;

var queryString = 'select count(*) as cnt from products'
getConnection().query(queryString, function (error2, data) {
if (error2) {
console.log(error2 + "메인 화면 mysql 조회 실패");
return
}
//전체 게시물의 숫자
totalPageCount = data[0].cnt

//현제 페이지
var curPage = req.params.cur;

console.log("현재 페이지 : " + curPage, "전체 페이지 : " + totalPageCount);


//전체 페이지 갯수
if (totalPageCount < 0) {
totalPageCount = 0
}

var totalPage = Math.ceil(totalPageCount / page_size);// 전체 페이지수
var totalSet = Math.ceil(totalPage / page_list_size); //전체 세트수
var curSet = Math.ceil(curPage / page_list_size) // 현재 셋트 번호
var startPage = ((curSet - 1) * 10) + 1 //현재 세트내 출력될 시작 페이지
var endPage = (startPage + page_list_size) - 1; //현재 세트내 출력될 마지막 페이지


//현재페이지가 0 보다 작으면
if (curPage < 0) {
no = 0
} else {
//0보다 크면 limit 함수에 들어갈 첫번째 인자 값 구하기
no = (curPage - 1) * 10
}

console.log('[0] curPage : ' + curPage + ' | [1] page_list_size : ' + page_list_size + ' | [2] page_size : ' + page_size + ' | [3] totalPage : ' + totalPage + ' | [4] totalSet : ' + totalSet + ' | [5] curSet : ' + curSet + ' | [6] startPage : ' + startPage + ' | [7] endPage : ' + endPage)

var result2 = {
"curPage": curPage,
"page_list_size": page_list_size,
"page_size": page_size,
"totalPage": totalPage,
"totalSet": totalSet,
"curSet": curSet,
"startPage": startPage,
"endPage": endPage
};


fs.readFile('list.html', 'utf-8', function (error, data) {

if (error) {
console.log("ejs오류" + error);
return
}
console.log("몇번부터 몇번까지냐~~~~~~~" + no)

var queryString = 'select * from products order by id desc limit ?,?';
getConnection().query(queryString, [no, page_size], function (error, result) {
if (error) {
console.log("페이징 에러" + error);
return
}
res.send(ejs.render(data, {
data: result,
pasing: result2
}));
});
});


})

})


//메인화면
router.get("/main", function (req, res) {
console.log("메인화면")
//main 으로 들어오면 바로 페이징 처리
res.redirect('/pasing/' + 1)

});

//삭제
router.get("/delete/:id", function (req, res) {
console.log("삭제 진행")

getConnection().query('delete from products where id = ?', [req.params.id], function () {
res.redirect('/main')
});

})
//삽입 페이지
router.get("/insert", function (req, res) {
console.log("삽입 페이지 나와라")

fs.readFile('insert.html', 'utf-8', function (error, data) {
res.send(data)
})

})
//삽입 포스터 데이터
router.post("/insert", function (req, res) {
console.log("삽입 포스트 데이터 진행")
var body = req.body;
getConnection().query('insert into products(name,modelnumber,series) values (?,?,?)', [body.name, body.num, body.section], function () {
//응답
res.redirect('/main');
})

})
//수정 페이지
router.get("/edit/:id", function (req, res) {
console.log("수정 진행")

fs.readFile('edit.html', 'utf-8', function (error, data) {
getConnection().query('select * from products where id = ?', [req.params.id], function (error, result) {
res.send(ejs.render(data, {
data: result[0]
}))
})
});

})
//수정 포스터 데이터
router.post("/edit/:id", function (req, res) {
console.log("수정 포스트 진행")
var body = req.body;
getConnection().query('update products set name = ?, modelnumber = ?, series = ? where id = ?',
[body.name, body.num, body.section, req.params.id], function () {
res.redirect('/main')
})
})


//글상세보기
router.get("/detail/:id", function (req, res) {
console.log("수정 진행")

fs.readFile('detail.html', 'utf-8', function (error, data) {
getConnection().query('select * from products where id = ?', [req.params.id], function (error, result) {
res.send(ejs.render(data, {
data: result[0]
}))
})
});


})

//mysql db 연결 함수

var pool = mysql.createPool({
connectionLimit: 10,
host: 'localhost',
user: 'root',
database: 'wow',
password: '1111'
})



//디비 연결 함수
function getConnection() {
return pool
}


module.exports = router


list.html(조회)

products.js에서 데이터를 처리한 후 뿌려주는 화면이 list.html이다. products.js 에서 게시물 조회 하는 로직 아래 

                res.send(ejs.render(data, {

                    data: result,

                    pasing: result2

                }));


이부분을 자세히 보자. mysql에서 가져온 데이터(result)와 페이징 관련 데이터 results2를 각각 data, pasing 키에 담았다! 이 키 값이 그대로 list.html로 넘어간다.


<!DOCTYPE html>
<html lang="ko">

<head>
<title>Bootstrap Example</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
</head>

<body>

<div class="container">
<h2>상품테이블</h2>

<a href="/insert">추가하기</a>
<br>
<br>
<p>상품테이블 목록입니다.</p>

<table class="table table-striped">
<thead>
<tr>
<th>아이디</th>
<th>상품명</th>
<th>넘버</th>
<th>분류</th>
<th>수정</th>
<th>삭제</th>
</tr>
</thead>
<tbody>

<% data.forEach(function (item,index){ %>
<tr>
<td>
<%= item.id %>
</td>
<td>
<a href='/detail/<%= item.id %>'><%= item.name %></a>
</td>
<td>
<%= item.modelnumber %>
</td>
<td>
<%= item.series %>
</td>
<td>
<a href="/delete/<%= item.id %>">삭제</a>
</td>
<td>
<a href="/edit/<%= item.id %>">수정</a>
</td>
</tr>
<% })%>
</tbody>
</table>
</div>


<br>
<br>


<div class="container">
<ul class="pager">



<%
var curSet = pasing.curSet
var endPage = pasing.endPage
var startPage = pasing.startPage
var totalSet = pasing.totalSet
var totalPage = pasing.totalPage
var page_list_size = pasing.page_list_size
var page_size = pasing.page_size
var curPage = pasing.curPage
%>


<%
if(curSet > 1){
%>

<li value=(startPage -1) class="previous">
<a href='/pasing/<%= ( startPage - 1) %>'>이전페이지</a>
</li>

<%
}
%>


<%
for(var i = startPage; i <= endPage; i++){
if(i > totalPage){break;}

if(i == curPage){
%>
<li value=<%= i %> class='' >
<a href='/pasing/<%= i %>'>
<%= i %>
</a>
</li>

<%
}else{
%>

<li value=<%= i %> class='active' >
<a href='/pasing/<%= i %>'>
<%= i %>
</a>
</li>


<%
}
%>

<%
}
%>

<%
if(curSet < totalSet){
%>
<li value=( endPage + 1) class="next">
<a href='/pasing/<%= ( endPage + 1) %>'>다음페이지</a>
</li>
<%
}
%>

</ul>
</div>

<br>
<br>

</body>

</html>


결과확인


나머지 화면 페이지부분은 게시판 페이징 기능 구현과 관련이 거의 없어서(?) 추가로 설명을 생략하겠습니다.

index.html

<!DOCTYPE html>
<html lang="en">
<head>
<title>Bootstrap Example</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
</head>
<body>

<div class="container">
<div class="jumbotron">
<h1>메인화면</h1>
<p> </p>
</div>
<p>.</p>
<a href="/main">메인화면으로</a>
</div>

</body>
</html>


insert.html

<!DOCTYPE html>
<html lang="en">
<head>
<title>물품관리</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
</head>
<body>

<div class="container">
<h2>상품입력</h2>
<p>.</p>
<form action="/insert" method="POST">
<div class="form-group">
<label for="name">상품이름:</label>
<input type="text" class="form-control" id="name" name= "name">
</div>
<div class="form-group">
<label for="num">번호</label>
<input type="text" class="form-control" id="num" name="num">
</div>
<div class="form-group">
<label for="section">분류</label>
<input type="text" class="form-control" id="section" name="section">
</div>
<button type="submit" class="btn btn-success">삽입하기</button>
</form>
</div>

</body>
</html>


edit.html

<!DOCTYPE html>
<html lang="en">
<head>
<title>물품관리</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
</head>
<body>

<div class="container">
<h2>상품수정</h2>
<p>나는 날마다 점점 더 좋아진다.</p>
<form method="POST">

<div class="form-group">
<label for="id">아이디:</label>
<input type="text" class="form-control" id="id" name= "id" value = "<%= data.id%>" disabled>
</div>

<div class="form-group">
<label for="name">상품이름:</label>
<input type="text" class="form-control" id="name" name= "name" value = "<%= data.name%>">
</div>
<div class="form-group">
<label for="num">번호</label>
<input type="text" class="form-control" id="num" name="num" value = "<%= data.modelnumber%>">
</div>
<div class="form-group">
<label for="section">분류</label>
<input type="text" class="form-control" id="section" name="section" value = "<%= data.series%>">
</div>
<button type="submit" class="btn btn-success">수정하기</button>
</form>
</div>

</body>
</html>


detail.html

<!DOCTYPE html>
<html lang="en">
<head>
<title>물품상제</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
</head>
<body>

<div class="container">
<h2>상품상세보기</h2>
<p></p>
<form method="POST">

<div class="form-group">
<label for="id">아이디:</label>
<input type="text" class="form-control" id="id" name= "id" value = "<%= data.id%>" disabled>
</div>

<div class="form-group">
<label for="name">상품이름:</label>
<input type="text" class="form-control" id="name" name= "name" value = "<%= data.name%>" disabled>
</div>
<div class="form-group">
<label for="num">번호</label>
<input type="text" class="form-control" id="num" name="num" value = "<%= data.modelnumber%>" disabled>
</div>
<div class="form-group">
<label for="section">분류</label>
<input type="text" class="form-control" id="section" name="section" value = "<%= data.series%>" disabled>
</div>
<!-- <button type="submit" class="btn btn-success">수정하기</button> -->
</form>
</div>

</body>
</html>


+ Recent posts

티스토리 툴바