DI (dependency injection)

 

인터페이스가 요구하는 명세를 구현한 클레스라면 어떤 클레스가 와도 결합이 가능한 점을 이용해

코드 작성부의 객체 간의 생성과 결합을 모두 외부 또는 수정이 용이한 곳으로 집중해 변경시마다 일일히

코드를 수정해야 하는 수고로움과 낭비를 줄이고 손쉽게 모듈(클레스)간의 변경,해제,결합,생성이 가능하게 한다

 

xml과 annotation을 사용한 injection 방법이 주가 되며, 오래된 프로그램의 경우 xml 방식인 경우가 많다

 

xml의 사용 방법은 html, el 등과 유사하며 객체 생성시에는 <bean> 태그를 속성으로는 id(변수명) class(클레스경로) 등을 사용한다. 객체 내부에 다른 객체를 결합시키는 경우 <property> 태그를 사용하고 속성으로는 name="setter", ref="참조변수" or value="값" 등을 사용한다. name의 경우에는 set 메소드가 EL처럼 set은 떼고 첫글자는 소문자로 변경되어 들어가니 헷갈리지 않아야 한다(클레스 내부에 setter는 당연히 작성해 둬야함)

이외 namespace를 설정해 처리기의 역할을 나누어 생성자 오버로드에 인자를 쉽게 넣어주거나 객체에 삽입하는 참조객체나 값을 <list> 로 넣는 등의 방법들이 있다.

 

annotation의 경우 MVC Model2을 참조해 내부적으로 계층이 나뉘어져 있으며 멤버객체의 선언부, 생성자, setter 메소드 등에 annotation을 붙일 수 있다. java config class를 생성해 component annotation을 지정해주면 해당 인터페이스에 결합된 하위 annotation 들은 자동으로 scan 범위에 포함되기 때문에 xml 설정 파일이 없이도 DI를 할 수 있다

 

 

 

AOP (Aspect Oriented Programming)

 

로그출력, 쓰레드 처리 속도나 예외처리 확인 같은 사용자가 아닌 개발자나 운영자가 필요로 하는 메인 서비스 외적인 측면의 코드들을 외부로 나누는 개념이다

 

기존에는 클래스 내부의 서비스 메소드 안에서 실행되야할 메인 기능의 위 아래에, 개발자나 운영자가 확인하고 싶은 내용을 구현하는 코드를 작성해, 확인하고 싶을 때는 주석을 해제하고 사용한 뒤, 사용이 끝난 뒤에는 다시 주석을 해두는 방법을 사용했다고 한다.

 

기존 Java에서 AOP를 구현하려면 Proxy 객체를 이용해 구현하려는 방식에 따라 인터페이스의 하위 객체들을 가져다 쓰면 된다.

 

Spring에서도 AOP 구현을 도와주는 라이브러리들이 있는데, AOP를 위한 Cross-cutting 클레스 몇개를 한번만 작성(로그확인, 소요시간확인 코드 등)해두면 DI와 맞물려 원본 클레스의 소스를 하나도 건드리지 않고 AOP 구현을 원하는 객체에 모듈처럼 삽입하고 빼는 형식으로 쉽게 사용할 수 있게된다.

 

 

프로젝트를 만들고 개발을 하는데 도움을 주는 빌드 도구

이클립스, intelij 등과 함께 사용하면 아주 유용하다

POM.XML에 필요한 라이브러리를 적어놓기만 하면 알아서 다운받고, 알아서 빌드 패스 설정하고, 알아서 해당 라이브러리가 필요로 하는 다른 필수 라이브러리까지 유효한 버전에 맞춰 다운 받고 설정해 준다.
IDE와 연동하면 maven 웹페이지에 갈 필요 없이 maven index를 리빌드 해두면 IDE에서 필요한 버전의 라이브러리를 검색하고 설치까지 할 수 있으며, 다른 컴퓨터에서 개발하거나 서비스 할 때는 POM.XML만 달랑 들고가면 된다.
(단, 본인이 작성한 클레스가 앱에 포함되어 있을 경우에는 그것도 포함해서 가져가던가, maven open repo에 deploy 해서 pom.xml에 포함시키든가 해야한다)

https://maven.apache.org/download.cgi

주소에서 Binary zip archive을 다운받는다

원하는 곳에 압축 풀고 시스템 path 환경변수를 설정해준다



IDE 없이 cmd에서 프로젝트 생성하기

프로젝트 생성을 원하는 디렉토리로 이동 -> 아래 명령어 입력

mvn archetype:generate -DgroupId=식별자그룹아이디 -DartifactId=프로젝트이름 -DarchetypeArtifactId=maven-archetype-quickstart

ex)
mvn archetype:generate -DgroupId=com.evagrim -DartifactId=javaprj -DarchetypeArtifactId=maven-archetype-quickstart

maven-archetype-quickstart은 메이븐에서 제공해주는 기본 형태의 아키타입 프로젝트이다
다른 사람이 만든 프로젝트 구조를 사용하고 싶을 경우 maven 사이트 검색이나 아키타입 id목록을 확인해서(mvn archetype:generate 명령어만 쳐주면 절차적 프로젝트 생성으로 사용 가능한 아키타입id가 주르륵 나온다) 위의 영역에 적어넣으면 됨

 

프로젝트 생성완료


명령어를 실행한 폴더에 javaprj이라는 프로젝트가 생성된다

 

 


maven 명령어는 pom.xml 설정 파일이 보이는 해당 디렉토리에서만 사용함

 



<maven phase 명령어 맛보기>

mvn compile
컴파일 한다 (컴파일까지의 phase를 모두 진행한다)
-compile 오류 뜨면 pom.xml에 사용하고 있는 jdk 버전을 명시해준다

 

POM.XML
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.evagrim</groupId>
  <artifactId>javaprj</artifactId>
  <packaging>jar</packaging>
  <version>1.0-SNAPSHOT</version>
  <name>javaprj</name>
  <url>http://maven.apache.org</url>
  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>
  </dependencies>
  <properties>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
  </properties>
</project>


다시 컴파일 하면 C:\project\maven\javaprj\target\classes 경로로 타고 들어가면 class 파일로 컴파일 된 것을 확인할 수 있다


mvn package
패키징 한다 (패키지까지 하는 과정들을 모두 수행한다, 컴파일 역시 포함)
C:\project\maven\javaprj\target에 .jar 파일로 만들어진다
-단순한 압축이라 zip으로 확장자를 바꾸면 내부를 확인해 볼 수 있음



<maven 프로젝트 패키지 실행방법>

프로젝트 경로로 이동한다

java -cp target\.jar파일명 App클래스경로

 

App 클래스 경로는 com.evagrim.App 이런식으로 작성

모르겠으면 .jar 파일 확장자를 zip으로 바꿔서 열어보거나 처음 프로젝트 생성할때 groupId로 정한걸 App 앞에 붙인다



<maven build lifecycle basics (메이븐 빌드 순서 -각 단계를 phase라 함-)>

validate
compile
test
package
verify
install
deploy

mvn ... 하면 그 하위 단계는 알아서 수행된다
install은 로컬 저장소에 자신이 만든 클레스 파일을 부품화(dependency)시켜서 사용할 때 쓰고
deploy는 maven 원격 저장소(global repo)에 올려서 다른 개발자들과 공유하는데 쓴다 (회원가입해야함)

빌드 순서는 POM.xml의 <packaging>jar</packaging> 설정에 따라 다르다
(<packaging>war</packaging>로 하면 웹아카이브(웹개발) 형식에 따른다)

자세한 내용은 maven 홈페이지에서 확인


각 단계(phase)를 실행하는 프로그램은 플러그인처럼 뺐다 꼽았다 할 수 있어 플러그인(plug-in)이라 부른다
이 플러그인들은 또한 하나가 여러 기능을 포함하고 있을 수 있어 그 중 하나의 기능을 goal이라 부른다
(goal은 플러그인 클레스 경로 뒤에 붙는다  -plugin:3.1:compile, -plugin:3.1:testCompile)

명령어
mvn help:describe -Dcmd=compile

컴파일까지 수행하는데 필요한 것들 기술해달라 요청


각 phase에 사용할 수 있는 plug-in들은 아래서 받을 수 있다 
https://maven.apache.org/plugins/index.html


 


<이클립스(IDE)에서 Maven 사용>

기존 maven 프로젝트를 가져올때는 그냥 import 하면 된다 (이클립스 EE 버전에는 깔려있음)

 

 


<plug-in 변경방법>

 

pom.xml

 

<build> </build> 태그 사이에 들어가 있는 것이 plugin 요청 사항이다

저러한 식으로 필요한 라이브러리를 적어넣으면 알아서 설치하거나 갈아끼거나 필요한 다른 라이브러리 체크하고 해준다

 

 


pom.xml이나 라이브러리를 수정하면 항상 maven update project를 해줘야함

 


<웹프로젝트로 변경하는 법>


pom.xml => <packaging>jar</packaging> = 변경 = <packaging>war</packaging>
수정 후 maven update
폴더 구성이 웹프로젝트에 적합하게 생성되고 변경되는 것을 볼 수 있다 (webapps 라는 웹용 root 폴더가 생긴다)
(톰캣 같은 서버 실행환경은 직접 설정해줘야 한다. web.xml 요구할테니 요구경로에 만들어줘야한다. webapp\web-inf 인가에 넣는 걸로 알고 있음)



<pom.xml에 다운 받고 설치할 라이브러리 지정해주는 방법들>

* Maven 로컬저장소 위치 (C:\Users(사용자)\userId\.m2\repository\org
- 라이브러리 파일이 깨지는 등의 문제가 생기면 repository 속의 내용물을 싹다 지워버리면 된다
(repository는 지우면 안된다. 그러면 다시 IDE를 켰을 때 메이븐에서 pom.xml을 보고 다시 설치한다)


1. 사이트에서 검색하고 추가하는 법

 

https://mvnrepository.com


mvn repo 사이트에서 필요한 라이브러리 + 버전을 검색해서 코드 복사 pom에 붙여넣기



2. 이클립스에서 바로 검색하고 추가하는 법

 

window 탭 > view를 열어야 하는데 other... 에 Maven repositories 창을 열어준다

 


사이트에 있는 Global Repo의 id를 받아와야 하기 때문에 인덱스 리빌드를 해준다 (1시간~5시간까지 걸릴 수 있음)

 


pom.xml 하단의 dependencies를 클릭해서 add를 누르면 검색할 수 있다 그 후 바로 추가해주거나

 


프로젝트 우클릭으로 maven 탭에서 추가해주면 됨

 


내가 만든 라이브러리를 설치하려면 라이브러리를 만든 뒤 Run > Run As > Maven install
을 통해 로컬저장소에 저장해주고 Local Repo 인덱스를 리빌드 한 뒤 추가해주면 된다

(딴 컴터에서 쓰려면 그냥 Export해서 .jar로 출력한담에 pom.xml과 같이 들고가던가 mvn deploy 하자)

'Development > Java' 카테고리의 다른 글

Spring MVC, 웹 개발환경 설정 #3  (0) 2022.08.26
DI, AOP #2  (0) 2022.08.25
Spring Framework, DI, IoC container #1  (0) 2022.08.23
List String Array, Transaction, Overload #12  (0) 2022.08.23
파일 등록과 사용 #11  (0) 2022.08.22

 


<Spring framework 사용의 핵심 >

Dependency Injection 기능과 Transaction Management 기능

jdbc의 기능만으로는 서비스 메소드로 나누어 놓은 connection을 공용으로 관리할 수가 없다
(기업형 application에서는 사용자가 많아질수록 필연적으로 서비스 함수 요청이 많아지고 그때마다 호출된 jdbc의 connection이 많아지면 transaction의 관리가 불가능하기 때문에 spring의 transaction management 기능을 사용한다)


<Spring의 기본 덕목>


MVC model 처리할 때는 DI(dependency injection)
Transaction 처리할 때는 AOP
인증과 권한 처리할 때는 Servlet Filter


<느슨한 결합력과 인터페이스 그리고 DI>

java 객체지향에서 배우는 내용
인터페이스로 실제 필요에 의해 생성되는 서비스 객체의 이름을 감추고, 외부설정 파일(XML, Annotation) 등을 사용해 사용되는 서비스 객체를 모듈화 시킨다. 그렇게 오버라이드로 인한 소스 수정을 억제하면서 객체간의 결합력을 낮춰 모듈간의 재사용성(컴파일이 완료된 bin(.jar)파일로의 전환)을 용이하게 한다. 

ex)
인터페이스 참조변수 = new (implement 인터페이스 된 서비스객체(feat. 외부설정에 따라 달라지는))
- 외부설정만 수정하면 소스 수정 없이 상속 받아 오버라이드하여 사용할 수 있게된다

Spring의 DI(의존성주입=부품조립)은 이 "느슨한 결합력과 인터페이스"를 위한 작업을 도와준다
-인터페이스 결합시 필요한 설정 파일을 제공해주고, 객체를 결합시켜주는 역할을 한다(객체 생성 & 조립)


<DI>

결합력을 낮춰야 하기 때문에 객체 간에는 composition has a(생성자에서 다른 객체를 생성)하는 방식 보다 setter를 사용하는 association has a 방식을 사용하며 이 클래스 내부의 부품 클래스를 DI에서는 dependency라고 한다)

injection 종류

setter를 이용한 setter injection
B b = new B();
A a = new A();
a.setB(b)
ex. 조립컴

construction을 이용한 construction injection
B b = new B();
A a = new A(b);
ex. 브랜드컴

위의 과정을 Spring의 DI가 대신해준다
DI = 종속성(의존성)주입 = 부품을 조립해준다


<IoC 컨테이너>

위의 부품(dependency)을 저장하고 조립해주는데 필요한 컨테이너다

XML, Annotation 등으로 필요한 목록을 작성해서 지시서를 만들면 주문받은 부품을 담을 그릇이 필요한데

그 부품들을 저장해서 담는 그릇이며 조립까지 해주는데, 기존의 import(큰 객체를 생성하면 필요한 작은 객체들까지 생성자에 의해 알아서 생성되는 방식)가 아닌 Inversion of Control(제어의역전)으로 조각 부품부터 만들어 큰 부품으로 조립해준다고 해서 IoC 컨테이너라 한다

 

'Development > Java' 카테고리의 다른 글

DI, AOP #2  (0) 2022.08.25
Maven 사용법  (0) 2022.08.24
List String Array, Transaction, Overload #12  (0) 2022.08.23
파일 등록과 사용 #11  (0) 2022.08.22
Servlet / Jsp admin page #10  (0) 2022.08.22

 

 

<문자열 제어할 때 쓰기 좋은 객체 및 서비스 메소드 몇개>


String array = array.trim();
// 양쪽 여백이 있을 시 삭제해줌
String[] arrays = arrays.split(" ");
// 사이에 여백이 있을 시 그것을 기준으로 배열에 나눠서 넣어줌
String[] arrays = arrays.trim().split(" ");
// 응용. 양쪽 여백을 지우고 사이에 있는 여백을 기준으로 나눠서 배열에 하나씩 넣어줌
List<String> a = Arrays.asList(string 배열);
// 배열로 굳이 2차원배열 같은 걸 돌릴 필요 없이 List의 서비스메소드들을 편하게 사용할 수 있음
a.removeAll(b)
// a리스트에서 b리스트를 빼서 a에 담는다
// 기존의 배열이 사라지기 때문에 a의 원본 배열은 두고 a라는 새로운 list 인스턴스로 만들어서 사용한다

sysout a_origin [84, 83, 82, 81, 80, 79, 78, 77, 76, 71]
sysout b [84, 83, 82, 81, 80, 71]
sysout a [79, 78, 77, 76]



<일괄공개 버튼 구현>

 

 

<일괄공개 버튼 구현을 위한 현재 페이지의 id를 모아놓는 히든 값 설정 (JSP+JSTL+EL)>

 

1
2
3
4
5
6
7
<c:set var="ids" value="" />
<c:forEach var="n" items="${list}">
<c:set var="ids" value="${ids} ${n.id}" />
</c:forEach>
<input type="hidden" name="ids" value="${ids}">
<input type="submit" class="btn-text btn-default" name="cmd" value="일괄공개">
<input type="submit" class="btn-text btn-default" name="cmd" value="일괄삭제">
cs

 

type="hidden" 을 걸어두면 사용자(클라이언트)에게는 보이지 않는다

 

<체크된 id들과 위에서 얻어온 페이지 id들을 사용한 조건처리 (Servlet)>

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
    
        String[] openIds = request.getParameterValues("open-id");
        String[] delIds = request.getParameterValues("del-id");
        String cmd = request.getParameter("cmd");
        String ids_ = request.getParameter("ids");
        String[] ids = ids_.trim().split(" ");
        // jsp에서 받아올 때 맨앞에도 빈공백이 하나 더 있어서 [ , id1, id2...] 이런식으로 나와버림
        // trim으로 양쪽 여백을 지운 상태에서 split 해야함 value="${ids} ${n.id}" 여기서 ids n.id 사이에 
        // 여백이 0번째에도 적용되서 그런 듯
        
        NoticeService service = new NoticeService();
        
        switch(cmd) {
        case "일괄공개":
            for(String openId : openIds)
                System.out.printf("open id : %s\n", openId);
            
            List<String> oids = Arrays.asList(openIds);
            List<String> cids = new ArrayList(Arrays.asList(ids));
            cids.removeAll(oids);
            //바로 Arrays.asList(ids).removeAll(oids) 하는 건 정적배열 상태라 안됨
            //그렇기에 바로 삭제나 추가를 할 수 없어 새로운 List에 담아 removeAll 해야함 
            System.out.println(ids_);
            System.out.println(Arrays.asList(ids));
            System.out.println(oids);
            System.out.println(cids);
            
            // 실제 Transaction 처리 부분이다
            // service.pubNoticeList(openIds);
            // service.closeNoticeList(cids);
            // 일괄공개 기능이라는 한가지의 논리적 기능을 수행하지만 DB로 쿼리 명령문을 전달하는 동작은 두 함수에 걸쳐 두번 실행된다
            // 어떠한 영향으로 둘중 하나의 동작만 실행 될 경우 그것이 Transaction 처리를 못한 것이 된다
            service.pubNoticeAll(oids, cids);
            // 바람직한 Transaction 처리 (이지만 서비스메소드 부분에서 아직 처리하지 않고 있다)
            
            break;
            
        case "일괄삭제":
            int[] ids1 = new int[delIds.length];
            for(int i=0; i<delIds.length;i++)
                ids1[i] = Integer.parseInt(delIds[i]);
            
            int result = service.deleteNoticeAll(ids1);
            break;
        }
        
        // post 처리 후 자신의 get요청을 호출해 url 재요청
        response.sendRedirect("list");
 
cs



<사용자(Controller) 편의성 제공을 위한 서비스 메소드 오버로드>

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
    public int pubNoticeAll(int[] oids, int[] cids){
        
        List<String> oidsList = new ArrayList<>();
        for (int i = 0; i < oids.length; i++) {
            oidsList.add(String.valueOf(oids));
        }
        List<String> cidsList = new ArrayList<>();
        for (int i = 0; i < cids.length; i++) {
            cidsList.add(String.valueOf(cids));
        }
        // 스트링 리스트 객체를 만든다. for문 돌려서 문자열로 형변환해서 차곡차곡 담는다
        // for문 하나로 해결될 걸 라이브러리를 쓰는 건 역으로 성능 저하를 일으킬 수 있다
        // 아래의 오버로드에서는 스트링 리스트 타입을 매개변수로 받고있다
        
        return pubNoticeAll(oidsList, cidsList);
    }
    
    public int pubNoticeAll(List<String> oids, List<String> cids){
        
        // 위에서 정수 배열 인자로 호출당한 친구가 만들어 보내준 스트링 리스트를 이어받는다 
        String oidsCSV = String.join(",", oids);
        String cidsCSV = String.join(",", cids);
        // CSV 형으로 만들어서 아래의 친구를 호출한다
        
        return pubNoticeAll(oidsCSV, cidsCSV);
    }
 
    public int pubNoticeAll(String oidsCSV, String cidsCSV){
        
        // 원하는 타입의 자료형을 얻었다
        
        int result = 0;
        
        String sqlOpen = String.format("update notice set pub=1 where id in (%s)", oidsCSV);
        String sqlClose = String.format("update notice set pub=0 where id in (%s)", cidsCSV);
        // "+odisCSV+" 이러한 형식이 보기 싫다면 위처럼 format 하면 된다
        
        try {
            
            Class.forName("oracle.jdbc.driver.OracleDriver");
            Connection con = DriverManager.getConnection(url, dID, dPW);
            Statement stOpen = con.createStatement();
            result += stOpen.executeUpdate(sqlOpen);
 
            Statement stClose = con.createStatement();
            result += stClose.executeUpdate(sqlClose);
            // 현재 Transaction 처리를 하고 있지 않음. 해야한다.
            
            stOpen.close();
            stClose.close();
            con.close();
            
        } catch (ClassNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        
        // DB에 잘 등록되었다면 적용된 행의 갯수를 반환한다
        return result;
    }
cs

 

oidsCSV에서 CSV의 뜻은 "콤마로 나눠진(Seperate) 값(Value)"이라는 뜻이라고 함
(하나의 문자열인데 "철수,민수,민지,슬기" 이런 식으로 들어있다)

'Development > Java' 카테고리의 다른 글

Maven 사용법  (0) 2022.08.24
Spring Framework, DI, IoC container #1  (0) 2022.08.23
파일 등록과 사용 #11  (0) 2022.08.22
Servlet / Jsp admin page #10  (0) 2022.08.22
JDBC 제어 oracleDB SQL 게시판용 몇개 #9  (0) 2022.08.21

 

<동일 form에서 파일 여러개 받을 때 html>

<th>첨부파일</th>
<tr><td colspan="3" class="text-align-left text-indent"><input type="file" name="file" /></td></tr>
<th>첨부파일</th>
<tr><td colspan="3" class="text-align-left text-indent"><input type="file" name="file" /></td></tr>
(그냥 중복 작성함)

 

<파일 업로드를 위한 인코딩 설정>

파일을 업로드 하기 위해선 form 태그에 enctype="multipart/form-data" 속성을
추가해줘야 한다.
(기본값 enctype="application/x-www-form-urlencoded")
-데이터 인코딩이 변경되면 서버에서 데이터를 받는 방식도 달라져야한다
(web.xml을 통한 전역설정과 annotation을 통한 설정 두가지 방법 존재)

web.xml 설정
<multipart-config>
  <location>/tmp</location>
  <max-file-size>20848820</max-file-size>
  <max-request-size>418018841</max-request-size>
  <file-size-threshold>1048576</file-size-threshold>
</multipart-config>

annotation 설정
@MultipartConfig(
//  location="",
  fileSizeThreshold=1024*1024,
  maxFileSize=1024*1024*5,
  maxRequestSize=1024*1024*5*5)

fileSizeThreshold 전송 데이터가 1mb 넘을 경우 location="/tmp" 디스크 사용
maxFileSize 보내지는 각각의 파일 한 개의 최대 크기(5mb) 제한
maxRequestSize 보내지는 전체 파일 용량의 최대 크기(25mb) 제한
디스크를 만들 location은 상대경로를 지원하지 않는다
(절대경로는 os에 따라 차이가 있으므로 기본값을 쓰자)

참고 : https://docs.oracle.com/javaee/6/tutorial/doc/gmhal.html


<클라이언트에서 서버로 파일 입력받아서 원하는 절대경로로 출력하기>

 

<단일 파일 업로드>

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
 
Part filepart = request.getPart("file");
String fileName = filepart.getSubmittedFileName();
InputStream fis = filepart.getInputStream();
// 단일 파일, 파일 이름 가져오기
        
String realPath = request.getServletContext().getRealPath("/upload");
// servlet 서비스의 실제 경로 가져오기
            
File path = new File(realPath);
if(!path.exists())
  path.mkdirs();
// 경로가 없다면 만든다
 
String filePath = realPath + File.separator + fileName;
// 절대 경로 설정하기
FileOutputStream fos = new FileOutputStream(filePath);
// 설정한 절대 경로로 파일 출력 경로 설정하기
        
byte[] buf = new byte[1024];
int size = 0;
while((size=fis.read(buf)) != -1)
fos.write(buf, 0, size);
// 1 byte 씩 읽어오면 하루종일 걸리니 byte 타입 배열 만들어서 1kb씩 받아오기
// 반복문 돌며 설정한 출력스트림에 파일 쓰기 (inputStream의 read() 메서드는 read 끝에 -1을 반환한다)
// 마지막 buf는 1024 byte 배열을 꽉 채울리가 없으니 write 메서드의 offset 설정으로 size만큼만 받아오고 while문 종료
        
fos.close();
fis.close();
// 다 썼으면 닫아준다
 
 
cs

 

 

<다중 파일 업로드>

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
 
Collection<Part> parts = request.getParts();
        
for(Part p : parts) {
  if(!p.getName().equals("file")) continue;
  if(p.getSize() == 0continue;
  // 파일이 0개 또는 1개만 올라올 경우
 
  Part filepart = p;
  String fileName = filepart.getSubmittedFileName();
  InputStream fis = filepart.getInputStream();
        
  String realPath = request.getServletContext().getRealPath("/upload");
 
  File path = new File(realPath);
  if(!path.exists())
    path.mkdirs();
  // 디렉토리가 존재하지 
 
  String filePath = realPath + File.separator + fileName;
 
  FileOutputStream fos = new FileOutputStream(filePath);
        
  byte[] buf = new byte[1024];
  int size = 0;
  while((size=fis.read(buf)) != -1)
  fos.write(buf, 0, size);
        
  fos.close();
  fis.close();
}
 
cs

 

 

<Db 저장을 위해 문자열로 변경>

 

StringBuilder builder = new StringBuilder();
-while문 밖에서 빌더 객체를 하나 만들어준다
builder.append(fileName);
builder.append(",");
-while문 안에서 파일이름을 합쳐준다
builder.delete(builder.length()-1, builder.length());
-파일 업로드가 모두 종료되면 마지막 파일 끝의 ","를 지워준다
notice.setFiles(builder.toString());
-setter files의 인자타입인 String으로 형변환해서 넣어준다

 

 

<html 영역>

<a download href="/upload/${fileName}">파일이름</a>

게시글 컨텐츠에 이미지를 넣고싶다면 게시글을 작성할때 html을 사용해준다
움짤 사진입니다<br >
<img src="/upload/img.gif">

보통 웹편집기를 삽입시켜 여러가지 방식으로 게시글 등록이 가능하게 한다

 

 


이외에 예외처리파일명 중복 검사 후 중복시 파일명 변경 등의 마감처리를 해야한다

'Development > Java' 카테고리의 다른 글

Spring Framework, DI, IoC container #1  (0) 2022.08.23
List String Array, Transaction, Overload #12  (0) 2022.08.23
Servlet / Jsp admin page #10  (0) 2022.08.22
JDBC 제어 oracleDB SQL 게시판용 몇개 #9  (0) 2022.08.21
JSTL #8  (0) 2022.08.15



admin page (required service method)


list 화면
removeNoticeAll(ids) 일괄삭제 요청
pubNoticeAll(ids) 일괄공개 요청

detail 화면
insertNotice(notice) 공지등록 요청
deleteNotice(id) 공지삭제 요청
updateNotice(notice) 공지수정 요청

index 화면
getNoticeNewestList()  페이지 요청



프로젝트 내에 동일한 클래스명을 가진 servlet이 있을 경우 eclipse가
제대로 된 url 맵핑을 해주지 못한다
-그래서 개발시에 일일히 url에 경로를 적어줘야 한다
편법) 
1. WEB-INF가 아닌 root폴더 아래 webServlet에서 맵핑하는 더미 폴더와 파일을 만들어준다
2. Project Explorer에서 더미 파일을 선택한 뒤 run 한다

403 보안 오류 (권한 문제)
404 url 오류 (서버의 맵핑 어노테이션이나 디스패쳐 포워드, html의 href를 보자)
405 메소드 오류 (서버를 보자)

이클립스에서는 boolean 타입의 getter를 만들때 is로 시작하는 메소드로 만든다
EL에서 문제가 될 수 있으니 get으로 바꿔준다



jdbc

insult, update, delete 를 사용할 때
Statement의 executeUpdate() 서비스 메소드를 사용한다.
받아올 데이터가 없기 때문에 ResulSet 객체는 필요없음
executeUpdate는 Db의 행삽입 행삭제 갯수 결과값을 int 값으로 return 한다
select 시에는 ResultSet rs = st.executeQuery(); 사용

DB의 컬럼을 추가하면 변경할 게 아주x& 많아진다
(미리 기능들의 기획을 잘해야함)



html

속성 method는 url로 데이터를 보내는 방식이고 서블릿 서버에서는 doGet doPost 등으로 받는다
속성 action은 메소드를 보내는 url이고 서버의 맵핑 주소
속성 name은 서버에서 map 타입 중 key이며 front, back 공통으로 parameter로 사용
속성 value는 해당 name(key)의 value 값이다 
-value는 null과 "" 빈문자열을 구분하기에 조건 처리를 둘다 해줘야 하며, html에서 전달된 내용은 문자열이기에 임시변수로 조건처리를 한 뒤 또는 할 때 형변환을 해서 사용한다

submit 버튼에도 name과 value 값을 줘서 서버로 보낼 수 있다
(submit 버튼이 두개라면 name(key) 값은 같게 해서 보내주는게 좋다)

<input type="file" name="file" />
input type을 파일로 하면 파일 선택하라고 윈도우 탐색기가 열림
type을 checkbox로 하고 name과 value를 넣으면 value 값 또는 null을 반환

동일한 url과 method로 서버로 요청을 보낼때는 form으로 묶어줌
(브라우저 호환성에 따라 action 속성을 넣어주지 않으면 에러가 날 수 있다)
(기본값은 요청한 url)

'Development > Java' 카테고리의 다른 글

List String Array, Transaction, Overload #12  (0) 2022.08.23
파일 등록과 사용 #11  (0) 2022.08.22
JDBC 제어 oracleDB SQL 게시판용 몇개 #9  (0) 2022.08.21
JSTL #8  (0) 2022.08.15
JSP, EL 보충 #7  (0) 2022.08.15

 

리스트(목록)용


최신 내림차순 게시글 리스트
"select * from ("
+ "select rownum NUM, N.* "
+ "from (select * from notice where "+field+" like ? order by regdate desc) N "
+ ") "
+ "where NUM between ? and ?";

게시글 수 가져오기
"select COUNT(ID) COUNT from ("
+ "select rownum NUM, N.* from (select * from notice where "+field+" like ? order by regdate desc) N "
+ ")";

 

게시글(내부)용


다음 게시글 가져오기
"select * from notice where id = ( "
+ "select id from notice where regdate > "
+ "(select regdate from notice where id=?)"
+ " and rownum=1 "
+ ")";

이전 게시글 가져오기
"select * from notice where id = ("
+ " select * from (select * from notice order by regdate desc)"
+ " where regdate < (select regdate from notice where id=?) and rownum=1"
+ " )";

 

 

게시글 목록 테이블 SQL과 댓글 테이블 SQL 합치기


집계함수(count) 등에 해당되는 select 주체에는 테이블.* 같은 *은 쓸 수 없다.

DB의 데이터 타입 중 CONTENT 등에 사용되는 CLOB 데이터 타입의 경우 몇 GB 까지 들어가는 데이터형이므로 JOIN 문장에서는 사용할 수 없다.
(JOIN해서 사용할 일이 집계가 대부분이므로 굳이 그렇게 모든 field를 가져올 필요도 없다)
(사용하려면 variable 캐릭터 형식으로 형변환 후 사용할 수 있으나 4000자 내로 잘리게 된다)
(잘리지 않게 하려면 개별적으로 요청한다)


위 내용을 토대로 게시글(notice) 목록 마다 댓글(comment) 수 집계해서 가져오기

select N.id, N.title, N.writer_id, N.regdate, N.hit, N.files, count(C.id) CMT_COUNT
from notice N
left join "COMMENT" C on N.id = C.notice_id
group by N.id, N.title, N.writer_id, N.regdate, N.hit, N.files
order by N.regdate desc;


기존에 게시글 목록 가져오던 SQL

select * from (
select rownum NUM, N.* 
from (select * from NOTICE where "+field+" like ? order by regdate desc) N 

where NUM between ? and ?;


둘을 합쳐야 한다 (굵게 표시된 곳에 넣어야 한다)


1) 댓글 집계수 컬럼 join한 테이블에서 order by절을 빼고 view로 만든다 
(view는 컬럼 추가정도만. 필터나 정렬은 빼는게 써먹기 좋음)

create view NOTICE_VIEW
as
select N.id, N.title, N.writer_id, N.regdate, N.hit, N.files, count(C.id) CMT_COUNT
from notice N
left join "COMMENT" C on N.id = C.notice_id
group by N.id, N.title, N.writer_id, N.regdate, N.hit, N.files
--order by N.regdate desc;


2) 리스트를 뽑을 때 가장 기준이 되는 테이블에 넣는다
select * from (
select rownum NUM, N.* 
from (select * from NOTICE_VIEW where "+field+" like ? order by regdate desc) N 

where NUM between ? and ?;


3) 기존에 만들어둔 서비스클래스로 가서 쿼리를 NOTICE_VIEW로만 고쳐주면 적용된다

 


4) 추가로 만든 notice_view 테이블에 관련된 서비스는 새로이 notice_view 테이블용 entity class를 따로 만들어 superclass로 notice를 상속받아 사용한다
(content는 db 데이터타입 문제로 지웠으니 빈문자열로 기본값을 넣어 사용)
(어차피 게시글 리스트에는 본문이 보이지 않는다. 필요한 것만 구현하자)
(기존 list 서비스용 객체 생성 notice_view로 전부 바꾸고 새로운 entity import)


'Development > Java' 카테고리의 다른 글

파일 등록과 사용 #11  (0) 2022.08.22
Servlet / Jsp admin page #10  (0) 2022.08.22
JSTL #8  (0) 2022.08.15
JSP, EL 보충 #7  (0) 2022.08.15
JSP EL(Expression Language) #6  (0) 2022.08.14

 

< JSTL (JSP Standard Tag Library) >


JSP View에서 사용할 수 있는 태그 라이브러리

https://www.javatpoint.com/jstl
JSTL 태그 기능 참고 사이트

https://mvnrepository.com/artifact/javax.servlet/jstl/1.2
JSTL 라이브러리 다운로드

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
jar파일 WEB-INF\lib 폴더에 추가 후 jsp 파일 상단에 위의 page지시자 추가



게시판 목록 생성

JSTL 사용 전)

1
2
3
4
5
6
7
8
9
10
11
12
13
<%
    List<Notice> list = (List<Notice>)request.getAttribute("list");
    for(Notice n : list){ 
    pageContext.setAttribute("n", n);
%>
<tr>
    <td>${n.id}</td>
    <td class="title indent text-align-left"><a href="detail?id=${n.id}">${n.title}</a></td>
    <td>${n.writerId}</td>
    <td>${n.regdate}</td>
    <td>${n.hit}</td>
</tr>
<%%>
cs


JSTL 사용 후)

1
2
3
4
5
6
7
8
9
<c:forEach var="n" items="${list}">
<tr>
<td>${n.id}</td>
<td class="title indent text-align-left"><a href="detail?id=${n.id}">${n.title}</a></td>
<td>${n.writerId}</td>
<td>${n.regdate}</td>
<td>${n.hit}</td>
</tr>
</c:forEach>
cs



 

JSTL 기능 범주


Core
Formating
Functions
SQL 
XML

(SQL, XML 기능은 MVC가 나오기 전 쓰던 방식으로 이제는 쓰지 않는 게 좋다)


유용한 JSTL 


<c:set>
<c:remove>
<c:if>
<c:choose>
<c:when>
<c:otherwise>
<c:forEach>
<c:forTokens>
<c:url>
<c:catch>

 

 

간단한 for 태그 라이브러리를 만들기 위한 코드


ForTag 클래스
TagSupport 상속
+doStartTag
+doEndTag
+doAfterBody
메소드 오버라이드


태그 Descriptor

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<taglib>
    <tlibversion>1.0</tlibversion>
    <jspversion>1.1</jspversion>
    <shortname>control</shortname>
    <uri>http://www.abcd.com/jsp/tags/control</uri>  // 고유식별자
    <info>제어구조 라이브러리</info>
 
    <tag>
    <name>for</name>
    <tagclass>
        com.abcd.web.taglib.ForTag
    </tagclass>
    <info>반복문</info>
    </tag>
</taglib>
cs


파일 path : WEB-INF\{파일명}.tld

 

JSP에서 JSTL 사용 시

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
uri = 고유식별자, prefix = 고유식별자가 너무 길기 때문에 fix
사용 - <c:for>


 

JSTL Core 기능

 

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

-page 지시자 추가

 


<c:forEach> 태그
var="n"                                  값을 담을 임시변수설정
items="${list}"                      저장소에서 꺼내올 배열
begin="0", end="9"              출력할 인덱스 범위 
varStatus="st"                      반복 중 현재 상태 값 확인                    

varStatus 속성 사용
%{st.current}                     현재 반복되는 아이템
%{st.index}                        현재 반복되는 인덱스
%{st.count}                        현재 반복 횟수
%{st.first}                          현재 아이템이 첫번째면 true
%{st.last}                           현재 아이템이 마지막이면 true
%{st.begin}                        begin 속성에 설정한 값
%{st.end}                           end 속성에 설정한 값
%{st.step}                         반복되는 인덱스의 증가치 (기본:1) (for문의 i++ 구간)

 


<c:set 태그>
-지역 변수를 만들 수 있다
<c:set var="page" value="${param.p}">


<c:if 태그>
-if 문, else 문은 없다
<c:if test="${조건식}">
실행 할 코드
</c:if>


JSTL 사용하여 pager 번호 생성해보기

 

1
2
3
4
5
6
7
8
<c:set var="page" value="${(empty param.p)?1:param.p}" />
<c:set var="startNum" value="${page-(page-1)%5}" />
 
<ul class="-list- center">
    <c:forEach var="i" begin="0" end="4">
        <li><a class="-text- orange bold" href="?p=${startNum+i}&t=&q=">${startNum+i}</a></li>
    </c:forEach>
</ul>
cs


현재 get으로 전달되는 page parameter를 매개로 page의 첫번째 숫자를 구한다.
${(empty param.p)?1:param.p} null일시 기본값을 1페이지로 해서 기본 게시판 페이지의 NullException을 막고
전달되어온 parameter를 기반으로 ${page-(page-1)%5}를 계산해 요청된 parameter의 첫번째 페이지를 구한다
아래에서 forEach로 반복하며 요청 시의 parameter 값 정의와 페이지 번호를 출력하는 용도로 써먹는다


JSTL 사용하여 추가로 pager 버튼 만들어보기


1
<c:set var="LastNum" value="23" />
cs

-MAX 페이지 수 임시 값 등록

다음 버튼

1
2
3
4
5
6
7
8
<div>
    <c:if test="${startNum+5 < LastNum}">
        <a href="?p=${startNum+5}&t=&q=" class="btn btn-next">다음</a>
    </c:if>
    <c:if test="${startNum+5 >= LastNum}">
        <span class="btn btn-next" onclick="alert('다음 페이지가 없습니다.');">다음</span>
    </c:if>
</div>
cs


이전 버튼

1
2
3
4
5
6
7
8
<div>
    <c:if test="${startNum > 1}">
        <a href="?p=${startNum-1}&t=&q=" class="btn btn-prev">이전</a>
    </c:if>
    <c:if test="${startNum <= 1}">
        <span class="btn btn-prev" onclick="alert('이전 페이지가 없습니다.');">이전</span>
    </c:if>
</div>
cs




JSTL forTokens로 첨부파일 목록 출력하기


(현재 Db에 파일명1.확장자1,파일명2,확장자2,파일명3,확장자3 형태로 커밋 해둔 상태)

1
2
3
4
5
6
7
8
9
10
11
<tr>
    <th>첨부파일</th>
    <td colspan="3" style="text-align:left; text-indent:10px;">
    <c:forTokens var="fileName" items="${n.files}" delims="," varStatus="st">
    <a href="${fileName}">${fileName}</a>
    <c:if test="${!st.last}">
        /
    </c:if>
    </c:forTokens>
    </td>
</tr>
cs



 

JSTL Format 기능

 

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


-page 지시자 추가


JSTL format 태그로 날짜 형식 변경하기

1
<td><fmt:formatDate pattern="yyyy-MM-dd hh:mm:ss" value="${n.regdate}" /></td>
cs


JSTL format 태그로 숫자 형식 변경하기

1
<td><fmt:formatNumber value="${n.hit}" /></td>
cs

-기본 형식은 세 자릿수 마다 , 로 끊음


https://www.javatpoint.com/jstl-fmt-formatnumber-tag
-참고 주소



JSTL functions 기능


https://www.javatpoint.com/jstl-function-tags
-참고 주소

사용예)

1
2
3
4
5
6
7
8
9
10
<c:forTokens var="fileName" items="${n.files}" delims="," varStatus="st">
    <c:set var="style" value="" />
    <c:if test="${fn:endsWith(fileName, '.zip')}">
        <c:set var="style" value="font-weight:bold; color:red;"/>
    </c:if>
    <a href="${fileName}" style="${style}">${fn:toUpperCase(fileName)}</a>
    <c:if test="${!st.last}">
    /
    </c:if>
</c:forTokens>
cs


${fn:endsWith()} 
-문자열 끝 찾아서 boolean 반환 (아이콘 등을 붙이는 데 사용할 수 있음)

${fn:toUpperCase()}
-대문자 변환

'Development > Java' 카테고리의 다른 글

Servlet / Jsp admin page #10  (0) 2022.08.22
JDBC 제어 oracleDB SQL 게시판용 몇개 #9  (0) 2022.08.21
JSP, EL 보충 #7  (0) 2022.08.15
JSP EL(Expression Language) #6  (0) 2022.08.14
JSP MVC model #5  (0) 2022.08.14

+ Recent posts