한 학원에서 교재로 사용하고 있는 것이다.



Posted by 세상을 살아가는 사람
,

한 학원에서 교재로 쓰고 있어서 어떤 책인지 찾아보았다. 괜찮은 것 같은데, 지은지가 오래되어 고민이다.

내가 가지고 있는 책과 비슷한 연도에 나온 것이라 최신 기술을 반영하지 못하고 있는 것 같아...



Posted by 세상을 살아가는 사람
,

웹 개발 시, 입력 파라미터에 대한 유효성 검사를 위해 정규표현식이 사용된다. 이에 대한 교재를 찾았다.



Posted by 세상을 살아가는 사람
,


Posted by 세상을 살아가는 사람
,


Posted by 세상을 살아가는 사람
,

나는 webiopi라는 라즈베리파이 상에서 web으로 GPIO를 제어하는 프로그램을 참조하여

자바 스프링으로 유사한 기능을 제어하려고 하고 있다.

webiopi는 REST API를 사용하여 GPIO를 제어하고 있어서 자바 스프링에서 REST API를 어떻게 처리하는지 파악하고 있다.

이 글에서는 webiopi에서 제공하는 REST API 기능에 대하여 자바 스프링으로 처리하기 위한 전 단계로 REST API 처리를 위한 Controller, Method 및 입력 파라미터 파싱에 대하여 알아본다.

1. Get GPIO function

  - HTTP GET /GPIO/(gpioNumber)/function

package com.talanton.rest.controller;


import org.springframework.http.HttpStatus;

import org.springframework.http.ResponseEntity;

import org.springframework.web.bind.annotation.PathVariable;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RequestMethod;

import org.springframework.web.bind.annotation.RestController;


@RestController

@RequestMapping("/GPIO")

public class GpioController {

@RequestMapping(value="/{gpioNumber}/function", method=RequestMethod.GET)

public ResponseEntity<String> getFunction(@PathVariable("gpioNumber") Integer gpioNumber) {

ResponseEntity<String> entity = null;

String result = gpioNumber + " : in";

entity = new ResponseEntity<String>(result, HttpStatus.OK);

return entity;

}

}


2. Set GPIO function

  - HTTP POST /GPIO/(gpioNumber)/function/("in" or "out" or "pwm")

@RequestMapping(value="/{gpioNumber}/function/{value}", method=RequestMethod.POST)

public ResponseEntity<String> postFunction(@PathVariable("gpioNumber") Integer gpioNumber, @PathVariable("value") String value) {

ResponseEntity<String> entity = null;

String result = gpioNumber + " : " + value;

entity = new ResponseEntity<String>(result, HttpStatus.OK);

return entity;

}


3. Get GPIO value

  - HTTP GET /GPIO/(gpioNumber)/value

@RequestMapping(value="/{gpioNumber}/value", method=RequestMethod.GET)

public ResponseEntity<String> getValue(@PathVariable("gpioNumber") Integer gpioNumber) {

ResponseEntity<String> entity = null;

String result = gpioNumber + " : 0";

entity = new ResponseEntity<String>(result, HttpStatus.OK);

return entity;

}


4. Set GPIO value

  - HTTP POST /GPIO/(gpioNumber)/value/(0 or 1)

@RequestMapping(value="/{gpioNumber}/value/{value}", method=RequestMethod.POST)

public ResponseEntity<String> postValue(@PathVariable("gpioNumber") Integer gpioNumber, @PathVariable("value") Integer value) {

ResponseEntity<String> entity = null;

String result = gpioNumber + " : " + value;

entity = new ResponseEntity<String>(result, HttpStatus.OK);

return entity;

}


5. Output a single pulse

  - HTTP POST /GPIO/(gpioNumber)/pulse/

@RequestMapping(value="/{gpioNumber}/pulse", method=RequestMethod.POST)

public ResponseEntity<String> postPulse(@PathVariable("gpioNumber") Integer gpioNumber) {

ResponseEntity<String> entity = null;

String result = gpioNumber + " : " + "pulse";

entity = new ResponseEntity<String>(result, HttpStatus.OK);

return entity;

}


6. Output bit sequence

  - HTTP POST /GPIO/(gpioNumber)/sequence/(delay),(sequence)

@RequestMapping(value="/{gpioNumber}/sequence/{delay},{sequence}", method=RequestMethod.POST)

public ResponseEntity<String> postSequence(@PathVariable("gpioNumber") Integer gpioNumber,

@PathVariable("delay") Integer delay, @PathVariable("sequence") String sequence) {

ResponseEntity<String> entity = null;

String result = gpioNumber + " : " + "sequence : " + delay + " : " + sequence;

entity = new ResponseEntity<String>(result, HttpStatus.OK);

return entity;

}


7. Output PWM with a duty cycle ratio

  - HTTP POST /GPIO/(gpioNumber)/pulseRatio/(ratio)

// 소수점 데이터를 입력받기 위한 방법 {variable:.+}

// 참고 : http://stackoverflow.com/questions/16332092/spring-mvc-pathvariable-with-dot-is-getting-truncated

@RequestMapping(value="/{gpioNumber}/pulseRatio/{ratio:.+}", method=RequestMethod.POST)

public ResponseEntity<String> postPulseRatio(@PathVariable("gpioNumber") Integer gpioNumber,

@PathVariable("ratio") Float ratio) {

ResponseEntity<String> entity = null;

String result = gpioNumber + " : pulseRatio : " + ratio;

entity = new ResponseEntity<String>(result, HttpStatus.OK);

return entity;

}


8. Output PWM with an angle for servos

  - HTTP POST /GPIO/(gpioNumber)/pulseAngle/(angle)

@RequestMapping(value="/{gpioNumber}/pulseAngle/{angle}", method=RequestMethod.POST)

public ResponseEntity<String> postPulseAngle(@PathVariable("gpioNumber") Integer gpioNumber,

@PathVariable("angle") Integer angle) {

ResponseEntity<String> entity = null;

String result = gpioNumber + " : pulseAngle : " + angle;

entity = new ResponseEntity<String>(result, HttpStatus.OK);

return entity;

}


9. Call a macro on the server

  - HTTP POST /macros/(macro)/(args)

왜냐하면 URL이 /macros로 시작하기 때문이다. 기존의 GpioController는 REST API URL이 /GPIO로 시작하였기 때문에 사용할 수 없고, 새로운 MacroController를 사용한다.

package com.talanton.rest.controller;


import org.springframework.http.HttpStatus;

import org.springframework.http.ResponseEntity;

import org.springframework.web.bind.annotation.PathVariable;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RequestMethod;

import org.springframework.web.bind.annotation.RestController;


@RestController

@RequestMapping("/macros")

public class MacroController {


@RequestMapping(value="/{macro}/{args}", method=RequestMethod.POST)

public ResponseEntity<String> macroFunction(@PathVariable("macro") String macro, @PathVariable("args") String args) {

ResponseEntity<String> entity = null;

String result = "macroFunction : " + macro + ", " + args;

entity = new ResponseEntity<String>(result, HttpStatus.OK);

return entity;

}

}


10. Get full GPIO state/configuration

  - HTTP GET /*

    /* 라는 URL을 처리하기 위하여 새로운 RestController StateController를 정의한다.

package com.talanton.rest.controller;


import org.springframework.http.HttpStatus;

import org.springframework.http.ResponseEntity;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RequestMethod;

import org.springframework.web.bind.annotation.RestController;


@RestController

@RequestMapping("")

public class StateController {


@RequestMapping(value="*", method=RequestMethod.GET)

public ResponseEntity<String> gpioState() {

ResponseEntity<String> entity = null;

String result = "getState";

entity = new ResponseEntity<String>(result, HttpStatus.OK);

return entity;

}

}


이제는 Webiopi에서 제공하는 REST API에 대한 처리 함수와 입력 파라미터 파싱을 완료하였다. 이후 실제 라즈베리파이에 적용하여 기능을 개발하는 것이 필요하다.

Posted by 세상을 살아가는 사람
,

나는 맨처음에 안드로이드 앱을 배울 때, 트위터에서 제공하는 REST Open API를 이용하여 트위터와 유사한 앱을 개발하는 것을 배웠다. 또한 현재 운영되고 있는 "한국인이 좋아하는 명시" 앱도 트위터에서 제공하는 REST API를 사용하고 있다. 벌써 6년 전의 일이지만 아직도 이 앱이 사용이 되고 있고, 오늘도 어떤 사용자가 이 REST Open API를 사용하여 앱 전체 사용자에게 자신이 창작한 시를 전송하였다.

이 글에서는 REST Open API에 대한 개념을 좀 더 보충하기 위하여 트위터에서 제공하는 REST Open API 목록을 기술하고자 한다.

POST


이 목록들을 살펴보면 유사한 기능들이 묶여 있는 것을 알 수 있으며 서로 연관관계가 있는 것을 알 수 있다.

REST API를 사용하여 서버의 기능을 Open 하려면 어떻게 URI 체계를 가져가야 하는지를 살펴볼 수 있다.

Posted by 세상을 살아가는 사람
,

나는 안드로이드를 2010년에 배우면서 Twitter Open API를 사용하는 것을 익히게 되었다. 트위터에서는 트윗을 보낼 때 Restful API를 사용하여 안드로이드 앱에서 서버로 트윗을 전송하고, 또 서버에 있는 트윗들을 앱으로 다운로드 한다. 이와 같이 트위터에서 자신들의 서버에 REST API를 제공하여 많은 어플리케이션을 개발할 수 있도록 하고 있다.

우리도 서버를 개발하면서 이와 같이 서버의 기능을 REST API를 통해 Open API로 제공하므로써 서비스의 이용을 촉진하고 수익의 증대를 가져올 수 있다. 따라서 REST API를 웹에서 제공하는 방법을 알아본다. 여기서는 jsp 방식의 스프링을 기반으로 기술한다.

REST는 'REpresentational State Transfer'의 약어로 하나의 URI는 하나의 고유한 리소스(Resource)를 대표하도록 설계한다는 개념이다.

REST API는 외부에서 특정 URI를 통해서 사용자가 원하는 정보를 제공하는 방식이다. 최근에 Open API에서 많이 사용되면서 REST 방식으로 제공되는 외부 연결 URI를 REST API라고 하고, REST 방식의 서비스 제공이 가능한 것을 'Restful'하다고 표현한다.

스프링은

- 버전 3부터 @ResponseBody annotation을 지원하면서 본격적으로 REST 방식의 처리를 지원

- 그 이전부터 스프링은 Content Negotiation 등을 이용해서 처리할 수 있다.

- 스프링 버전 4에 들어와서 @RestController가 본격적으로 소개됨


1. @RestController의 소개

  - 스프링 4에서 REST 방식의 데이터 처리를 위해서 Controller 차원의 annotation을 지원

  - 스프링 3까지는 컨트롤러에서 @Controller annotation을 사용해서 처리하고, 화면 처리를 담당하는 JSP가 아닌 데이터 자체를 서비스하려면 해당 메소드나 리턴 타입에 @ResponseBody annotation을 추가하는 형태로 작성


2. Sample project 연습

우선 @RestController를 사용하여 jsp view 형태가 아니라 문자열의 형태로 서버로부터 응답을 받는 방법을 기술한다. 반환되는 형태는 단순 문자열 또는 JSON, XML 형태를 가진다.

2.1 테스트용 컨트롤러 생성하기

  - 새로운 스프링 MVC 프로젝트를 생성

    + 이클립스에서 File>New>Spring Legacy Project 선택

    + Project name을 입력하고, Templates로 Spring MVC Project 선택하고 Next 버튼 클릭

    + 적절한 package name을 입력하고, Finish 버튼을 클릭

  - pom.xml 수정

    + Properties에서 java-version : 1.6 -> 1.8

    + Properties에서 org-springframework-version : 3.1.1.RELEASE -> 4.1.7.RELEASE

  - Project Facets 수정

    + 이클립스에서 프로젝트를 선택하고 우클릭하고 Properties 선택

    + Project Facets 선택하고 Java Version을 1.8로 변경하고 Runtimes에서 Apache Tomcat v8.5 선택하고 OK 버튼 클릭

  - @RestController로 SampleController 생성

    + java source에서 새로운 package 생성 : com.example.restful.controller

    + 기존 HomeController를 controller package 밑으로 이동하고 package 정보를 맞추어 준다.

    + 새로운 SampleController 생성

package com.example.restful.controller;


import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RestController;


@RestController                    // REST API 기능을 처리하기 위해 @RestController annotation을 사용

@RequestMapping("/sample")  // path를 /sample로 지정

public class SampleController {


}


2.2 단순 문자열의 경우

  - @RestController에서 문자열 데이터는 기본적으로 브라우저에는 'text/html' 타입으로 처리된다.

  - 단순 문자열 처리를 위한 REST API를 만든다. (/sample/hello)

@RestController // REST API 기능을 처리하기 위해 @RestController annotation을 사용

@RequestMapping("/sample") // path(URI)를 /sample로 지정

public class SampleController {


@RequestMapping("/hello")    // path(URI)를 /sample/hello로 지정

public String sayHello() {        // method는 class내에서 path와 유사하게, 유닉하게 작명

return "Hello World!";    // 문자열(text/html) "Hello World!" 형태를 응답

}

}


  - contextPath 변경

    + 보통 REST API는 URI "/sample/hello"와 같이 contextPath가 "/"로 정의된다. 따라서 이클립스에서 default로 생성되는 contextPath를 변경해 주어야 한다.

    + Servers 창에서 Tomcat v8.5 Server at localhost를 double click하면 "Tomcat v8.5 Server at localhost" 창이 편집창에 생긴다. 여기서 Modules로 변경한 후, Web Modules에서 생성한 프로젝트를 선택 후 "Edit" 버튼을 클릭한다.

    + Path를 "/"로 수정하고 저장한다.

  - 다시 프로젝트를 선택하여 Tomcat을 구동한다. 그러면 "localhost:8080/"으로 구동됨을 확인할 수 있다.

  - 브라우저에서 "localhost:8080/sample/hello"를 입력하면 아래 그림과 같이 "Hello World!"가 출력됨을 확인할 수 있다.


2.3 객체를 JSON으로 반환하는 경우

  - 반환하는 형태가 문자열의 형태가 아니라 JSON 형태로 반환하는 방법을 기술한다.

  - 예를 들기 위하여 간단한 객체를 생성 (SampleVO)

package com.example.restful.domain;


public class SampleVO {

  private Integer mno;

  private String firstName;

  private String lastName;

  public Integer getMno() {

return mno;

  }

  public void setMno(Integer mno) {

this.mno = mno;

  }

  public String getFirstName() {

return firstName;

  }

  public void setFirstName(String firstName) {

this.firstName = firstName;

  }

  public String getLastName() {

return lastName;

  }

  public void setLastName(String lastName) {

this.lastName = lastName;

  }

  @Override

  public String toString() {

return "SampleVO [mno=" + mno + ", firstName=" + firstName + ", lastName=" + lastName + "]";

  }

}

  - 객체를 반환하는 REST API를 생성

  @RequestMapping("/sendVO")    // JSON 형식으로 객체 SampleVO를 반환하는 "/sample/sendVO" 작성

  public SampleVO sendVO() {       // SampleVO를 반환하는 sendVO method 정의

SampleVO vo = new SampleVO();     // SampleVO를 생성

vo.setMno(123);                            // 데이터를 삽입

vo.setFirstName("길동");

vo.setLastName("홍");

return vo;                                    // SampleVO 객체를 반환

  }


  - JSON 형태로 객체를 반환하기 위해서는 jackson-databind 라이브러리를 pom.xml에 추가로 정의해야 함

  <dependency>

<groupId>com.fasterxml.jackson.core</groupId>

<artifactId>jackson-databind</artifactId>

<version>2.8.4</version>

  </dependency>


  - 브라우저에서 접속 결과

   위 그림과 같이 JSON 형태의 SampleVO가 반환됨을 알 수 있다. 또한 아래 그림을 보면, Response Headers의 Content-Type으로 application/json 형태임을 확인할 수 있다.


2.4 컬렉션 타입의 객체를 반환하는 경우

  - 결과 데이터로 List 형태가 제공되는 경우에 대한 예를 들어 본다.

  @RequestMapping("/sendList")     // List 형태의 데이터를 반환하는 예 (/sample/sendList)

  public List<SampleVO> sendList() {    // List<SampleVO> 형태의 리스트를 반환하는 method sendList

List<SampleVO> list = new ArrayList<SampleVO>(); // List 생성

for(int i = 0;i < 10;i++) {    // 예로 10개의 SampleVO를 담는다.

SampleVO vo = new SampleVO(); // SampleVO를 생성

vo.setMno(i); // 데이터를 담는다.

vo.setFirstName("길동" + i);

vo.setLastName("홍");

list.add(vo); // list에 객체를 추가한다.

}

return list; // List 객체를 반환

  }

  - 아래 그림과 같이 JSON 형태로 List<SampleVO> list가 반환된다.


  - 결과 데이터로 Map 형태가 제공되는 경우에 대한 예를 들어 본다.

  @RequestMapping("/sendMap")

  public Map<Integer, SampleVO> sendMap() { // Map 형태의 데이터 반환하는 예 (/sample/sendMap)

Map<Integer, SampleVO> map = new HashMap<Integer, SampleVO>(); // Map 생성

for(int i = 0;i < 10;i++) { // 예로 10개의 SampleVO를 map에 추가

SampleVO vo = new SampleVO(); // SampleVO 생성

vo.setMno(i); // 데이터를 담는다.

vo.setFirstName("길동" + i);

vo.setLastName("홍");

map.put(i, vo); // Map에 SampleVO를 추가

}

return map; // Map 데이터를 반환

  }

  - 아래 그림은 Map 형태의 데이터를 JSON 형식으로 반환하는 결과이다.


Posted by 세상을 살아가는 사람
,

예전에 웹을 학원에서 배울때 강사가 정규 표현식에 대하여 간단하게 알려 주었고, 회원가입이나 로그인 시 입력 파라미터에 대한 유효성 검증을 할 때 사용했던 것으로 기억한다.

그때는 정확히 모르고 그냥 내가 필요한 것만 적용해서 사용했던 것 같다. 여기서는 이에 대하여 정리를 해보고 여러 가지 사용 예에 대하여 검토를 해 보고자 한다. (참고 : 위키백과 정규 표현식)

1. 정의

  정규 표현식 또는 정규식은 특정한 규칙을 가진 문자열의 집합을 표현하는데 사용하는 언어 형식

  많은 텍스트 편집기와 프로그래밍 언어에서 문자열의 검색과 치환을 위해 지원

  자바스크립트 : 기능 내장

  자바 : 표준 라이브러리로 제공

2. 기본 개념

  주로 패턴으로 부르는 정규 표현식은 특정 목적을 위해 필요한 문자열 집합을 지정하기 위해 쓰이는 식

- 불린 "또는" : 수직선은 여러 항목 중 선택을 하기 위해 구분

  예) gray|grey -> gray 또는 grey와 일치

- 그룹 묶기 : 괄호를 사용하면 연산자의 범위와 우선권을 제어

  예) gray|grey와 gr(a|e)y는 'gray'나 'grey' 집합을 둘 다 기술하는 동일 패턴

- 양의 지정

  ?            : 0또는 1차례까지의 발생을 의미

    예) colou?r는 'color'와 'colour'를 둘다 일치

  *            : 0번 이상의 발생을 의미

    예) ab*c는 'ac', 'abc', 'abbc', 'abbbc' 등을 일치

  +           : 1번 이상의 발생을 의미

    예) ab+c는 'abc', 'abbc', 'abbbc' 등을 일치시키지만, 'ac'는 일치시키지 않음

  {n}         : 정확히 n번만큼 일치시킨다.

  {min,}     : 'min'번 이상만큼 일치

  {min,max} : 적어도 'min'번 만큼 일치시키지만 'max'번을 초과하여 일치시키지는 않는다.

3. 문법

3.1 POSIX 기본 및 확장 표준 문법

- 특수문자(?) : 

  + 외부 : 12개 ('.', '[', ']', '^', '$', '(', ')', '\', '*', ',', '{', '}', '-') 13개(?)

  + 내부 : 4개의 문자 ("\", "^", "-", "]") 자바와 닷넷은 "["를 포함


3.2 POSIX 확장 문법


3.3 문자 클래스


4. 예

- email : /^[-A-Za-z0-9_]+[-A-Za-z0-9_.]*[@]{1}[-A-Za-z0-9_]+[-A-Za-z0-9_.]*[.]{1}[A-Za-z]{2,5}$/

  + 처음 문자로 '-' 또는 대문자, 소문자, 숫자, '_' 로 시작 : 1회 이상(+)

  + 그 다음 문자로 '-', 대문자, 소문자, 숫자, '_' 문자가 0번 이상 (*)

  + '@' 문자가 정확히 1번은 일치

  + 그 다음 문자로 '-', 대문자, 소문자, 숫자, '_'가 1회 이상 (+)

  + 그 다음 문자로 '-', 대문자, 소문자, 숫자, '_', '.'가 0번 이상 (*)

  + '.' 문자는 정확히 1번은 일치

  + 마지막으로 문자로 대문자, 소문자가 최소 2번 이상 5번 초과 안되게

- 비밀번호 : /^(?=.*[a-zA-Z])(?=.*[!@#$%^*+=-])(?=.*[0-9]).{7,16}$/

  + 알파벳, 특수문자, 숫자가 최소 7개 이상 최대 16번 미만 입력

  + 특수문자, 영문, 숫자를 한번은 입력하되 8자 이상 16자 미만...


5. JSP 프로젝트 예

5.1 join.jsp (회원가입)

<script type="text/javascript" charset="utf-8" src="../js/member/join.js"></script>

  <form action="joinPro.jsp" method="post">

    <input type="email" id="id" name="id" placeholder="아이디 입력">

    <input type="password" id="pw" name="pw" placeholder="암호 입력">

    <input type="submit" value="회원가입">

  </form>

5.2 join.js

window.onload = function() {

  var id = document.getElementById("id");

  id.onkeyup = chkId;    // 키보드로 id 값을 입력하면 chkId 함수가 불린다.

  var pw = document.getElementById("pw");

  pw.onkeyup = chkPw;    // 키보드로 password를 입력하면 chkPw 함수가 불린다.

}

var chkId = function() {

var idReg = /^[-A-Za-z0-9_]+[-A-Za-z0-9_.]*[@]{1}[-A-Za-z0-9_]+[-A-Za-z0-9_.]*[.]{1}[A-Za-z]{2,5}$/;

if( !idReg.test(id.value) ) {    // 사용자가 입력한 것이 원하는 값이 아니면 'false'로 표시

id.style.color = "#EE5656";

passId = false;

}

else {                            // 원하는 패턴을 만족하면 'true'로 표시

id.style.color = "blue";

passId = true;

}

}


var chkPd = function() {

var pwReg = /^(?=.*[a-zA-Z])(?=.*[!@#$%^*+=-])(?=.*[0-9]).{7,16}$/;

if( !pwReg.test(pwd.value) ) {    // 사용자가 입력한 것이 원하는 패턴이 아니면 'false'로 표시

pwd.style.color = "#EE5656";

passPw1 = false;

}

else {                                // 원하는 패턴을 만족하면 'true'로 표시

pwd.style.color = "blue";

passPw1 = true;

}

}


정확한 사용은 조금 더 공부가 필요한 것 같네요...

Posted by 세상을 살아가는 사람
,

앞에서 기술했던 부분에 이어 특정 게시판에 대한 관리 기능(게시글 목록 보기, 게시글 추가)을 기술한다. 다른 기능(게시글 수정, 게시글 삭제, 게시글 읽기)은 기술하지 않았다.

1. 게시글 목록 보기

- 헤더의 메뉴 중 "지식창고"에 마우스를 갖다대면 서브 메뉴가 나타난다. 이는 추가된 게시판에 대한 게시글 관리 기능으로의 링크이다. 이 링크를 누르면 해당 게시판에 대한 게시글 관리창으로 이동하게 된다.

예를 들어, "Web"이라는 서브 메뉴를 누르면 "http://localhost:8080/board/article/list?board_id=1"로 이동하게 된다.

1.1 ArticleController에서 /article/list에 대한 처리

package com.example.board.controller;


import javax.inject.Inject;

import javax.servlet.http.HttpServletRequest;

import org.springframework.stereotype.Controller;

import org.springframework.ui.Model;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RequestMethod;

import com.example.board.domain.ArticleVO;

import com.example.board.domain.Criteria;

import com.example.board.domain.PageMaker;

import com.example.board.service.ArticleService;

import com.example.board.service.BoardService;


@Controller

@RequestMapping(value="/article")

public class ArticleController {

@Inject

public ArticleService service;    // 게시글 관리 기능을 처리하기 위한 Service

@Inject

public BoardService bService;    // 게시판 관리 기능을 처리하기 위한 Service

@RequestMapping(value="/list", method=RequestMethod.GET)

public void list(int board_id, Criteria cri, Model model) throws Exception {

model.addAttribute("board", bService.read(board_id));    // 해당 게시판에 대한 정보를 획득

model.addAttribute("list", service.list(cri));      // 해당 게시판의 게시글 목록 정보를 가져온다.

PageMaker pm = new PageMaker();            // 페이징 처리를 위한 인스턴스를 만든다.

pm.setCri(cri);                                         // 페이징 처리를 위한 기준 정보를 저장

pm.setTotalCount(service.listCount());           // 해당 게시판의 전체 게시글의 수를 얻는다.

model.addAttribute("pageMaker", pm);         // 페이징 처리를 위한 정보를 model에 저장

}


1.2 list.jsp : 게시글 목록 보기 화면


<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>

<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>

<!DOCTYPE html>

<html>

<head>

<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0" />

<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">

<title>게시글 목록 보기</title>

<link rel="stylesheet" href="${pageContext.request.contextPath}/resources/css/common.css"/>

<link rel="stylesheet" href="${pageContext.request.contextPath}/resources/css/boardlist.css"/>

</head>

<body>

<header> <!-- 헤더 영역 정의 -->

  <jsp:include page="../common/header.jsp" flush="false"/>

</header>

<section id="wrap"> <!-- 본문 영역 정의 -->

  <section id="lnav"> <!-- 사이드 메뉴 영역 정의 -->

    <jsp:include page="../common/lnav.jsp" flush="false"/>

  </section>

  <section id="content"> <!-- 내용 영역 정의 -->

    <h1>게시판 이름 : ${board.board_name}</h1>

    <table>

      <c:if test="${pageMaker.totalCount > 0}"><!-- 게시글이 있는지 검사 -->

      <tr>

<td colspan="5"><!-- 페이지에 대한 정보를 표시 -->

  ${pageMaker.cri.pageStart}-${pageMaker.cri.pageEnd}

  [${pageMaker.cri.page}/${pageMaker.totalPageCount}]

</td>

      </tr>

      </c:if>


      <c:choose>

      <c:when test="${list.size() == 0}"><!-- 게시글이 없을 때 안내문 표시 -->

<tr>

  <td colspan="5">

    게시글이 없습니다.

  </td>

</tr>

      </c:when>

      <c:otherwise>

<tr><!-- 게시글에 대한 표시 타이틀 부분 -->

  <td class="title_head" width="9%">번호</td>

  <td class="title_head" width="54%">제목</td>

  <td class="title_head" width="11%">작성자</td>

  <td class="title_head" width="17%">작성일</td>

  <td class="title_head" width="9%">조회수</td>

</tr>

<c:forEach var="article" items="${list}"><!-- 각 게시글에 대하여 -->

    <tr>

      <td>${article.article_id}</td>

      <td width="40%">

<c:if test="${article.depth > 0}"><!-- 댓글에 대한 depth 표시 -->

  <c:forEach begin="1" end="${article.depth}">-</c:forEach>&gt;

</c:if>

<c:if test="${article.secret == 'T'}"><!-- 제목 처리 (비밀글 여부에 따라 다르게 처리)-->

  <c:choose>

  <c:when test="${sessionScope.member.category == 'admin'}">

    <c:set var="query" value="article_id=${article.article_id}&page=${pageMaker.cri.page}

&perPageNum=${pageMaker.cri.perPageNum}"/>

    <a href="<c:url value="/article/read?${query}"/>">${fn:substring(article.title, 0, 28)}</a>

  </c:when>

  <c:when test="${sessionScope.id == article.email}">

    <c:set var="query" value="article_id=${article.article_id}&page=${pageMaker.cri.page}

&perPageNum=${pageMaker.cri.perPageNum}"/>

    <a href="<c:url value="/article/read?${query}"/>">${fn:substring(article.title, 0, 28)}</a>

  </c:when>

  <c:otherwise>

    ${fn:substring(article.title, 0, 28)}

  </c:otherwise>

  </c:choose>

  &nbsp;<img width="13px" height="13px" alt="*" src="<c:url

   value="/resources/images/secret_image.png"/>">

        </c:if>

        <c:if test="${article.secret != 'T'}">

  <c:choose>

  <c:when test="${board.read_allow == 'all'}">

    <c:set var="query" value="article_id=${article.article_id}&page=${pageMaker.cri.page}

&perPageNum=${pageMaker.cri.perPageNum}"/>

    <a href="<c:url value="/article/read?${query}"/>">${fn:substring(article.title, 0, 28)}</a>

  </c:when>

  <c:when test="${board.read_allow == 'login' && sessionScope.id != null}">

    <c:set var="query" value="article_id=${article.article_id}&page=${pageMaker.cri.page}

&perPageNum=${pageMaker.cri.perPageNum}"/>

    <a href="<c:url value="/board/read?${query}"/>">${fn:substring(article.title, 0, 28)}</a>

  </c:when>

  <c:when test="${board.read_allow == 'login' && sessionScope.id == null}">

    ${fn:substring(article.title, 0, 28)}

  </c:when>

  <c:when test="${sessionScope.id != null && sessionScope.member.category == 

    board.read_allow}">

    <c:set var="query" value="article_id=${article.article_id}&page=${pageMaker.cri.page}

&perPageNum=${pageMaker.cri.perPageNum}"/>

    <a href="<c:url value="/article/read?${query}"/>">${fn:substring(article.title, 0, 28)}</a>

  </c:when>

  <c:otherwise>

    ${fn:substring(article.title, 0, 28)}

  </c:otherwise>

          </c:choose>

        </c:if>

      </td>

      <td>${article.writer_name}</td>

      <td><fmt:formatDate value="${article.createdAt}" pattern="yyyy-MM-dd" /></td>

      <td>${article.read_count}</td>

    </tr>

    <c:choose>

    <c:when test="${board.display_format==2}"><!-- 본문의 표시 방법 처리 -->

      <tr>

        <td colspan="5">${fn:substring(article.content, 0, 200)}</td>

      </tr>

    </c:when>

    <c:when test="${board.display_format==3}">

      <tr>

        <td colspan="5">${article.content}</td>

      </tr>

    </c:when>

            </c:choose>

          </c:forEach>

          <tr>

            <td colspan="5"><!-- 페이징 처리(링크 표시) -->

              <c:if test="${pageMaker.prev == true}">

  <a href="<c:url value="/article/list?board_id=${board.board_id}&page=

 ${pageMaker.startPage-1}&perPageNum=${pageMaker.cri.perPageNum}"/>">이전</a>

              </c:if>

              <c:forEach var="pno" begin="${pageMaker.startPage}" end="${pageMaker.endPage}">

  <a href="<c:url value="/article/list?board_id=${board.board_id}&page=${pno}

 &perPageNum=${pageMaker.cri.perPageNum}" />">[${pno}]</a>

        </c:forEach>

              <c:if test="${pageMaker.next == true}">

  <a href="<c:url value="/article/list?board_id=${board.board_id}&page=

  ${pageMaker.endPage+1}&perPageNum=${pageMaker.cri.perPageNum}"/>">다음</a>

              </c:if>

            </td>

          </tr>

        </c:otherwise>

</c:choose>

      <c:choose><!-- 글쓰기 권한에 따른 글쓰기 허용여부 처리 -->

<c:when test="${board.write_allow == 'all'}">

  <tr>

    <td colspan="5"><a href="write?board_id=${board.board_id}">글쓰기</a></td>

  </tr>

</c:when>

<c:when test="${board.write_allow == 'login' && sessionScope.id != null}">

  <tr>

    <td colspan="5"><a href="write?board_id=${board.board_id}">글쓰기</a></td>

  </tr>

</c:when>

<c:when test="${sessionScope.id != null && sessionScope.member.category == 

board.write_allow}">

  <tr>

    <td colspan="5"><a href="write?board_id=${board.board_id}">글쓰기</a></td>

  </tr>

</c:when>

      </c:choose>

    </table>

  </section>

</section>

<footer> <!-- 풋터 영역 정의 -->

  <jsp:include page="../common/footer.jsp" flush="false"/>

</footer>

</body>

</html>


게시글 쓰기 링크를 누르면 게시글 쓰기로 이동한다. (/article/write)


1.3 ArticleController /article/write 처리

  @RequestMapping(value="write", method=RequestMethod.GET)

  public String write(int board_id, Model model) {

model.addAttribute("board", bService.read(board_id)); // 해당 게시판에 대한 정보를 획득

return "/article/writeForm"; // 게시글 입력 창으로 이동

  }


1.4 writeForm.jsp : 게시글 입력 폼 화면


위 그림과 같이 게시글 쓰기 입력 창을 보여준다.

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<!DOCTYPE html>

<html>

<head>

<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0" />

<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">

<title>게시글 쓰기</title>

<link rel="stylesheet" href="${pageContext.request.contextPath}/resources/css/common.css"/>

<link rel="stylesheet" href="${pageContext.request.contextPath}/resources/css/articlewrite.css"/>

<script type="text/javascript" src="<c:url value="/resources/js/jquery-1.11.3.min.js"/>"></script>

<script type="text/javascript" charset="utf-8" src="<c:url value="/resources/js/articlewrite.js"/>"></script>

</head>

<body>

<c:choose>

<c:when test="${param.article_id != null}">

<c:set var="num" value="${param.article_id}"/>

<c:set var="ref" value="${param.ref}"/>

<c:set var="step" value="${param.step}"/>

<c:set var="depth" value="${param.depth}"/>

</c:when>

<c:otherwise>

<c:set var="num" value="0"/>

<c:set var="ref" value="1"/>

<c:set var="step" value="0"/>

<c:set var="depth" value="0"/>

</c:otherwise>

</c:choose>

<header> <!-- 헤더 영역 정의 -->

<jsp:include page="../common/header.jsp" flush="false"/>

</header>

<section id="wrap"> <!-- 본문 영역 정의 -->

  <section id="lnav"> <!-- 사이드 메뉴 영역 정의 -->

    <jsp:include page="../common/lnav.jsp" flush="false"/>

  </section>

  <section id="content"> <!-- 내용 영역 정의 -->

    <c:if test="${num == 0}">

      <h2>게시글 작성</h2>

    </c:if>

    <c:if test="${num != 0}">

      <h2>댓글 작성</h2>

    </c:if>

    <form action="<c:url value='write' />" method="post" id="frm">

      <input type="hidden" name="board_id" value="${param.board_id}">

      <input type="hidden" name="num" value="${num}">

      <input type="hidden" name="ref" value="${ref}">

      <input type="hidden" name="step" value="${step}">

      <input type="hidden" name="depth" value="${depth}">

      <input type="hidden" name="author" value="">

      <table>

<tr>

  <td>제목: </td>

  <td>

    <c:if test="${num == 0}">

      <input type="text" name="title" size="90" maxlength="60" placeholder="제목을 입력 하십시

요." required/>

    </c:if>

    <c:if test="${num != 0}">

      <input type="text" name="title" size="90" maxlength="60" value="[답변]"/>

    </c:if>

  </td>

</tr>

<c:if test="${sessionScope.id != null}">

  <tr>

    <td>작성자: </td>

    <td><label>${sessionScope.member.nickname}</label></td>

  </tr>

  <input type="hidden" name="writer_name" value="${sessionScope.member.nickname}"/>

  <input type="hidden" name="email" value="${sessionScope.member.id}">

  <input type="hidden" name="password" value="${sessionScope.member.password}"/>

</c:if>

<c:if test="${sessionScope.id == null}">

  <tr>

    <td>작성자: </td>

    <td><input type="text" name="writer_name" /></td>

  </tr>

  <input type="hidden" name="email" value="이메일">

  <tr>

    <td>글암호: </td>

    <td><input type="password" name="password" /></td>

  </tr>

</c:if>

<tr>

  <td style="margin:0; padding:0;" colspan="2"><textarea name="content" rows="5" cols="80" 

  style="width:100%; height:212px;"></textarea></td>

</tr>

      </table>

      <input id="addBtn" type="submit" value="전송" />

    </form>

  </section>

</section>

<footer> <!-- 풋터 영역 정의 -->

<jsp:include page="../common/footer.jsp" flush="false"/>

</footer>

</body>

</html>


1.5 articlewrite.js : 첨부 파일을 5개 까지 추가할 수 있도록 dynamic하게 파일 첨부 버튼을 조작

  생략


[마치며] 처음에는 잘 정리해야 겠다고 생각을 했는데, 내용이 방대하여 마음의 여유가 없어지고, 조급해져 일부 내용을 빠트리고 정리가 된 것 같다. 글쎄 다른 사람이 이글을 보고 이해할 수 있을지 염려도 되지만 source code를 추가로 첨부해 본다.

bdmanager.zip


Posted by 세상을 살아가는 사람
,