(103)

Servlet이란?

WEB과 WAS의 차이, WEB이란? WAS란? https://mag1c.tistory.com/299 https://mag1c.tistory.com/300 작은 프로젝트를 두번, 개인 프로젝트를 한번 진행하면서 모두 Apache-Tomcat 서버를 사용하였다. 기존에 알고 있는 바로는 다음과 같다. 1. Apache : WEB Ser mag1c.tistory.com JSP란? (Java Server Pages) https://mag1c.tistory.com/298 https://mag1c.tistory.com/300 JSP (Java Server Pages) JSP(Java Server Pages) HTML내에 자바 코드를 삽입하여 웹 서버에서 동적으로 웹 페이지를 생성하여 웹 브라우저에 돌려주는 서버 ..

JSP란? (Java Server Pages)

WEB과 WAS의 차이, WEB이란? WAS란? https://mag1c.tistory.com/299 https://mag1c.tistory.com/300 작은 프로젝트를 두번, 개인 프로젝트를 한번 진행하면서 모두 Apache-Tomcat 서버를 사용하였다. 기존에 알고 있는 바로는 다음과 같다. 1. Apache : WEB Ser mag1c.tistory.com Servlet이란? WEB과 WAS의 차이, WEB이란? WAS란? https://mag1c.tistory.com/299 https://mag1c.tistory.com/300 작은 프로젝트를 두번, 개인 프로젝트를 한번 진행하면서 모두 Apache-Tomcat 서버를 사용하였다. 기존에 알고 있는 바로 mag1c.tistory.com JSP ..

싱글톤(Singleton) 패턴

싱글톤 패턴이란? 클래스의 인스턴스가 오직 1개만 생성되는 디자인 패턴이다. public class SingletonPattern { private static SingletonPattern instance = new SingletonPattern(); //생성자는 private private SingletonPattern() { } public static SingletonPattern getInstance() { return instance; } public void what() { System.out.println("싱글톤패턴"); } public static void main(String[] args) { SingletonPattern s1 = SingletonPattern.getInstance(..

[Spring] 스프링이란? 스프링의 특징(제어의 역전(IoC), 의존성 주입(DI), 관점지향 프로그래밍(AOP), POJO)

스프링 JAVA의 웹 프레임워크이다. Java 언어를 기반으로 사용한다. Java로 다양한 어플리케이ㅐ션을 만들기 위한 프로그래밍 틀이다. Java의 활용도가 높아지면서 JSP, MyBatis, JPA등의 기술이 생겨났다. Spring은 앞서 말한 기술들을 더 쉽게 사용할 수 있게 해주는 오픈소스 프레임워크이다. 프레임워크(FrameWork) 어떠한 목적을 달성하기 위해 복잡하게 얽혀있는 문제를 해결하기 위한 구조이며, 개발에 있어 하나의 뼈대 역할을 한다. 애플리케이션 개발 시 필수적인 코드, 알고리즘, DB 커넥션 등의 기능들을 위해 뼈대를 제공한다. 구체적인 설계와 구현을 재사용이 가능하게끔 상호 협력하는 클래스와 인터페이스의 집합이다. 라이브러리(Library) 라이브러리는 주로 소프트웨어를 개발..

Spring MVC 구조 / MVC1 MVC2

MVC (Model - View - Controller) 소프트웨어 공학에서 사용되는 소프트웨어 디자인 패턴이다. Model 모든 데이터 정보를 가공하여 가지고 있는 컴포넌트 상태 변화가 있을 때 컨트롤러와 뷰에 통보한다. (뷰는 최신 결과를 리턴, 컨트롤러는 적용 가능한 명령을 추가,제거,수정) View 사용자가 보는 시각적인 UI 요소 Controller Model과 View를 연결 해 주는 역할 Model과 View에 명령을 보낸다. MVC-1 View와 Controller을 JSP가 담당한다. JSP에서 MVC가 모두 이루어져 재사용성, 가독성이 떨어져 유지보수성이 낮다. MVC-2 JSP에서 모든 것을 수행하던 MVC1 패턴과 달리 M, V, C의 역할이 분리되어 있다. 오류 발생 혹은 수정 시..

Spring MVC 프로젝트 폴더의 구조

MVC 프로젝트 폴더의 구조 src/main/java 자바 파일이 모여있는 곳 (Controller, service, vo, dao, dto, mapper, api..) src/main/resources 클래스의 리소스들을 보관하는 곳 DB연결을 위한 자원, 의존성 주입을 위한 XML 파일 등 자바 코드와 관련된 모든 것들을 보관한다 src/test 테스트를 위한 자바 파일 및 리소스를 보관하는 곳 Maven Dependencies 메이븐에서 자동으로 관리 해 주는 라이브러리 폴더로 pom.xml에 작성된 라이브러리들을 자동으로 다운받아 관리한다. src 웹 관련 자원들이 담겨 있는 루트 폴더로 test는 테스트 공간이다. src/main/webapp/resources 웹에 필요한 js, css, img..

Spring MVC Project 셋팅

프로젝트 만들기 1. New - Other에서 legacy검색 후 Spring Legacy Project 생성 2. 프로젝트 이름을 작성하고, Templates - Spring MVC Project 선택 후 Next 3. top-level package를 정의해주면 된다. pom.xml 사용할 버전과 pom.xml에 적혀 있는 버전이 다르다면 맞춰주면 된다. pom.xml (pom : project object model) Maven의 빌드 정보를 담고 있는 파일 Maven 자바 프로젝트를 관리하는 툴 (자바 소스를 컴파일하고 패키징해서 deploy까지 자동화 해 준다.) 미리 작성된 xml 파일을 이용하여 라이브러리를 자동으로 다운하거나 프로젝트를 빌드 해 준다. 해당 작업까지 완료한 후 1. Proj..

27. 검색 기능 - 점프 투 스프링부트(게시판 만들기)

해당 게시글은 점프 투 스프링부트 교재를 통한 개인 학습 용도이며 기초 세팅은 생략하였습니다. 자바 8, 스프링부트 2.7.7버전 입니다. [ 검색 기능 ] Specification 여러 테이블에서 데이터를 검색해야 할 경우에는 JPA가 제공하는 Specification 인터페이스를 사용하는 것이 편리하다. Specification을 어떻게 사용할 수 있는지 예제를 통해서 알아보자. Specification Specification은 보다 정교한 쿼리의 작성을 도와주는 JPA의 도구이다. https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#specifications QuestionService에 search 메서드를 추가하고, getL..

Jsoup(1) - Jsoup이란? / 초간단 예시

Tech/Java,Kotlin,Spring 2023. 5. 11. 09:48
728x90
728x90
 

jsoup: Java HTML parser, built for HTML editing, cleaning, scraping, and XSS safety

jsoup: Java HTML Parser jsoup is a Java library for working with real-world HTML. It provides a very convenient API for fetching URLs and extracting and manipulating data, using the best of HTML5 DOM methods and CSS selectors. jsoup implements the WHATWG H

jsoup.org

 

 

 

 

Jsoup은 실제 HTML 작업을 위한 Java 라이브러리이다.

HTML5 DOM 메서드와 CSS selector을 사용하여 URL을 가져오고 데이터를 추출 및 조작하기 위한 매우 편리한 API를 제공한다.

 

1. URL, 파일 또는 문자열에서 HTML 스크랩 및 파싱

2. DOM 순회 또는 CSS select를 사용한 데이터 찾기 및 추출

3. HTML요소, 속성 및 텍스트 조작

4. XSS 공격을 방지하기 위해 수신 허용 목록에 대해 사용자 제출 콘텐츠 정리

5. 깔끔한 HTML 출력

을 제공해준다고 한다.

 

 

 

설치

아래의 링크에서 다운로드받아서 프로젝트 내부에 JAR을 추가하거나 Maven이나 Gradle에 주입하여 사용한다.  

나는 자바 프로젝트를 사용해서, JAR을 추가했다.

 

Download and install jsoup

Download and install jsoup jsoup is available as a downloadable .jar java library. The current release version is 1.16.1. What's new See the 1.16.1 release announcement for the latest changes, or the changelog for the full history. Previous releases of jso

jsoup.org

 

 

 

 

 

사용해보기

우선 공식 라이브러리 사이트에서 제공해주는Example 코드를 사용해보자.

package org.jsoup.examples;

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;

import java.io.IOException;

public class jsoupexam {
    public static void main(String[] args) throws IOException {
        Document doc = Jsoup.connect("http://en.wikipedia.org/").get();
        log(doc.title());

        Elements newsHeadlines = doc.select("#mp-itn b a");
        for (Element headline : newsHeadlines) {
            log("%s\n\t%s", headline.attr("title"), headline.absUrl("href"));
        }
    }

    private static void log(String msg, String... vals) {
        System.out.println(String.format(msg, vals));
    }
}

위키디피아의 메인에서, 뉴스 섹션의 헤드라인 목록들을 가져온다고는 하는데

헤드라인인지는 모르겠으나 여튼 굵은 앵커태그의 title속성값과 href값을 가져오는데 성공했다.

 

728x90
300x250
mag1c

mag1c

2년차 주니어 개발자.

Servlet이란?

Tech/Java,Kotlin,Spring 2023. 4. 9. 06:40
728x90
728x90
 

WEB과 WAS의 차이, WEB이란? WAS란?

https://mag1c.tistory.com/299 https://mag1c.tistory.com/300 작은 프로젝트를 두번, 개인 프로젝트를 한번 진행하면서 모두 Apache-Tomcat 서버를 사용하였다. 기존에 알고 있는 바로는 다음과 같다. 1. Apache : WEB Ser

mag1c.tistory.com

 

JSP란? (Java Server Pages)

https://mag1c.tistory.com/298 https://mag1c.tistory.com/300 JSP (Java Server Pages) JSP(Java Server Pages) HTML내에 자바 코드를 삽입하여 웹 서버에서 동적으로 웹 페이지를 생성하여 웹 브라우저에 돌려주는 서버 사이

mag1c.tistory.com

 

 

 

Servlet


자바 서블릿

자바를 사용하여 웹 페이지를 동적으로 생성하는 서버측 프로그램 혹은 그 사양을 말하며, 흔히 서블릿이라 불린다.

서블릿은 자바 코드안에 HTML을 포함하고 있다.

 

서블릿은 웹에 다양한 기능이 요구되면서 정적인 자료 뿐 아니라 동적인 페이지들을 만들 필요가 생겼기 때문에 만들어졌다. 서블릿은 클라이언트의 요청에 맞춰 동적인 결과를 만들어 주는 자바 웹 프로그래밍 기술이며, 서블릿은 WAS의 서블릿 컨테이너 안에서 동작하게 된다.

 

 

특징

1. 클라이언트의 요청에 동적으로 응답하는 웹 어플리케이션 요소(Component)

2. HTML을 사용하여 응답

3. JAVA의 Thread 이용

4. MVC 패턴의 Controller 역할을 함

5. HTTP 프로토콜 서비스를 지원하는 javax.servlet.http.HttpServlet클래스를 상속받는다

6. UDP보다 처리 속도가 느림

7. HTML변경 시 서블릿을 재컴파일해야 한다.

 

 

서블릿(웹) 컨테이너


웹 컨테이너(서블릿 컨테이너)와 동작과정

 

서블릿 컨테이너는 서블릿을 담고 관리해주는 컨테이너다. 서블릿 클래스의 규칙에 맞게 서블릿을 관리하며, 클라이언트의 요청을 받으면 HttpServletRequest와 HttpServletResponse 객체를 생성하여 post, get에 따라 동적인 페이지를 생성하여 응답한다.

 

 

1. 서블릿 생명주기 관리

서블릿의 생명주기를 관리한다. 서블릿 클래스를 로딩하여 인스턴스화하고 초기화 메서드를 호출하고, 요청이 들어오면 적절한 서블릿 메서드를 찾아서 동작한다. 또한 서블릿의 생명이 다하면 가비지 컬렉션을 통해 메모리에서 제거한다.

 

2. 통신 지원

웹 서버와 소켓을 만들어서 클라이언트의 요청을 받고 응답할 수 있는 통신을 지원해 준다. 통신을 하기 위한 listen, accept등의 과정을 API로 제공하여 복잡한 과정을 생략해주기 때문에 개발자가 비즈니스 로직 개발에 집중할 수 있게 도와준다.

 

3. 멀티쓰레드 지원 및 관리

클라이언트의 요청을 받을 때마다 새로운 자바 스레드를 생성한다. 그래서 동시에 여러 요청이 들어와도 멀티쓰레딩 환경에서 동시다발적 작업을 관리할 수 있다.

 

4. 보안 관리

보안 관련 기능을 제공하기 때문에 개발자는 서블릿에 보안 관련 메서드를 구현하지 않아도 된다.

 

 

서블릿의 동작 과정

1. 클라이언트의 요청
2. HttpServletRequest, HttpServletResponse 객체 생성
3. Web.xml이 어느 서블릿에 대해 요청한 것인지 탐색
4. 해당하는 서블릿에서 service()메서드 호출
5. doGet()또는 doPost()호출
6. 동적 페이지 생성 후 ServletResponse 객체에 응답 전송
7. HttpServletRequest, HttpServletResponse 소멸

 

 

서블릿의 생명 주기


서블릿 생명주기 메서드

init() / 초기화
서블릿 요청 시 맨 처음 한번만 호출됨

doGet(), doPost() / 작업 메서드
서블릿 요청 시 매번 호출되며, 클라이언트가 요청한 것에 맞춰 실행됨

destroy() / 종료
메모리에서 소멸될 때 호출된다.

 

1. 서블릿 객체 생성

최초 호출 시 Tomcat(WAS)에서 서블릿 파일을 불러와 컴파일한 후 메모리에 로드한다.

Tomcat은 서블릿 파일이 변경되는게 아니라면 서버 종료 전까지 해당 객체를 계속 재활용한다.

JSP파일이나 서블릿파일이 수정된다면 서블릿 자원을 해제시킨 뒤 새로운 객체를 생성해서 메모리에 로드하는 형식이다.

 

2. 선처리 작업 수행(객체 생성 시 최초 1회 수행)

서블릿에서 메서드를 임의로 작성하고 @PostConstruct 어노테이션을 부여하여 가장 먼저 해당 메서드를 실행한다.

서블릿 객체가 생성될 때 먼저 처리하고 싶은 로직이 있다면 만들어준다.

init()메서드로 수행해도 큰 문제는 없으나, 성격이 다른 로직이라면 별도로 분리해서 작성 가능하다.

@PostConstruct

의존성 주입이 이루어진 후 초기화를 수행하며, service 로직을 수행하기 전에 발생한다.

 

3. init() 실행

GenericServlet 클래스에서 정의하는 메서드

서블릿 객체 생성 시 초기화 작업을 수행하는 역할

Tomcat 서버 환경설정 파일인 web.xml의 내용을 불러와 ServletConfig 객체로 생성해준다.

오버로딩 구조로 되어있고 Tomcat에서 ServletConfig객체를 파라미터로 넣어 실행하며, init()메서드를 오버라이딩하지 않으면 아무것도 실행시키지 않는다.

 

4. Service() : 자원 해제 전까지 요청이 올때마다 반복 실행

클라이언트에서 요청이 오면, 요청에 맞춰 GET, POST등이 실행된다.

 

5. destroy() : 소멸

컨테이너가 서블릿에 종료 요청 시 실행되며, 종료시에 처리해야할 작업들은 destroy() 메서드를 오버라이딩하여 구현한다.

 

 

 

서블릿 작성 예시


 

위의 그림처럼, 프로젝트 선택 후 New - Servlet을 선택하여 생성하고, 패키지명, 클래스명과 URL 매핑, 메서드를 선택하여 만들면 되는데, 자세한 사항은 해당 블로그를 참조하자. (링크)

 

package com.java.example;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * Servlet implementation class gunchim
 */
@WebServlet("/gunchim")
public class gunchim extends HttpServlet {
	private static final long serialVersionUID = 1L;
       
    /**
     * @see HttpServlet#HttpServlet()
     */
    public gunchim() {
        super();
        // TODO Auto-generated constructor stub
    }

	/**
	 * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// TODO Auto-generated method stub
		response.setContentType("text/html;charset=euc-kr");
        PrintWriter out=response.getWriter();
        out.println("<html>"+"<body>"+"<h1>서블릿이다 </h1> "+"</body>"+"</html>");
	}

	/**
	 * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// TODO Auto-generated method stub
		doGet(request, response);
	}

}

 

/main으로 요청 시 com.java.example.gumchim라는 서블릿으로 연결해서 보여줄 수 있도록 web.xml의 하단에 설정 코드를 추가해준다.

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" id="WebApp_ID" version="4.0">
  <display-name>Servlet</display-name>
  <welcome-file-list>
    <welcome-file>index.html</welcome-file>
    <welcome-file>index.htm</welcome-file>
    <welcome-file>index.jsp</welcome-file>
    <welcome-file>default.html</welcome-file>
    <welcome-file>default.htm</welcome-file>
    <welcome-file>default.jsp</welcome-file>
  </welcome-file-list>
     <servlet>
      <servlet-name>Name</servlet-name>
      <servlet-class>com.java.example.gunchim</servlet-class>
   </servlet>
   
   <servlet-mapping>
      <servlet-name>Name</servlet-name>
      <url-pattern>/class</url-pattern>
   </servlet-mapping>
    
    
   <servlet>
      <servlet-name>ServletTest</servlet-name>
      <servlet-class>com.java.example.gunchim</servlet-class>
   </servlet>
   
   <servlet-mapping>
      <servlet-name>ServletTest</servlet-name>
      <url-pattern>/main</url-pattern>
   </servlet-mapping>
</web-app>

 

 

 

 

 

참조

https://ko.wikipedia.org/wiki/%EC%9E%90%EB%B0%94_%EC%84%9C%EB%B8%94%EB%A6%BF

https://code-lab1.tistory.com/210

https://velog.io/@falling_star3/Tomcat-%EC%84%9C%EB%B8%94%EB%A6%BFServlet%EC%9D%B4%EB%9E%80

 

728x90
300x250
mag1c

mag1c

2년차 주니어 개발자.

JSP란? (Java Server Pages)

Tech/Java,Kotlin,Spring 2023. 4. 8. 06:03
728x90
728x90
 

WEB과 WAS의 차이, WEB이란? WAS란?

https://mag1c.tistory.com/299 https://mag1c.tistory.com/300 작은 프로젝트를 두번, 개인 프로젝트를 한번 진행하면서 모두 Apache-Tomcat 서버를 사용하였다. 기존에 알고 있는 바로는 다음과 같다. 1. Apache : WEB Ser

mag1c.tistory.com

 

Servlet이란?

WEB과 WAS의 차이, WEB이란? WAS란? https://mag1c.tistory.com/299 https://mag1c.tistory.com/300 작은 프로젝트를 두번, 개인 프로젝트를 한번 진행하면서 모두 Apache-Tomcat 서버를 사용하였다. 기존에 알고 있는 바로

mag1c.tistory.com

 

 

JSP (Java Server Pages)


JSP(Java Server Pages)

HTML내에 자바 코드를 삽입하여 웹 서버에서 동적으로 웹 페이지를 생성하여 웹 브라우저에 돌려주는 서버 사이드 스크립트 언어이다.

 

JSP 모델 2의 구조

 

JSP로 작성된 프로그램은 서버로 요청 시 서블릿 파일로 변환되어 JSP 태그를 분해하고 추출하여 다시 순수한 HTML을 변환한다. JSP를 통해 정적인 HTML과 동적으로 생성된 컨텐츠(httprequest params)를 혼합하여 사용할 수 있으며, 사용자가 입력한 컨텐츠에 맞게 동적인 웹 페이지를 생성한다.

 

JSP는 실행 시 자바 서블릿으로 변환된 후 실행되므로 서블릿과 거의 유사하다고 볼 수 있지만, 서블릿과는 달리 HTML 표준에 따라 작성되므로 웹 디자인하기에 편리하다. 서블릿은 자바코드 내에 HTML 코드가 있어서 읽고 쓰기가 불편하여 작업의 효율성이 떨어진다.

 

 

특징

1. 스크립트 언어이기 때문에 자바 기능을 그대로 사용할 수 있다

2. Tomcat(WAS)이 만들어 놓은 객체(request, response, session 등)를 사용하고, 수정된 경우 재배포하지 않아도 Tomcat(WAS)이 알아서 처리해준다.

3. 사용자 정의 태그를 사용하여, 보다 효율적으로 웹 사이트를 구성할 수 있다.(JSTL)

4. HTML코드 안에 Java코드가 있기 때문에 HTML코드를 작성하기 쉽다.

 

 

 

참조

https://ko.wikipedia.org/wiki/JSP

https://sesangcoding.tistory.com/entry/JSP%EB%9E%80-Java-Server-Pages

728x90
300x250
mag1c

mag1c

2년차 주니어 개발자.

싱글톤(Singleton) 패턴

Tech/Java,Kotlin,Spring 2023. 3. 28. 06:33
728x90
728x90

싱글톤 패턴이란?


클래스의 인스턴스가 오직 1개만 생성되는 디자인 패턴이다.

public class SingletonPattern {

	private static SingletonPattern instance = new SingletonPattern();
	
        //생성자는 private
	private SingletonPattern() {
		
	}
	
	public static SingletonPattern getInstance() {
		return instance;
	}
	
	public void what() {
		System.out.println("싱글톤패턴");
	}
	
	public static void main(String[] args) {		
		SingletonPattern s1 = SingletonPattern.getInstance();
	}
	
}

 

 

장점

1. 여러 클래스에서 해당 클래스의 생성자를 호출해도 처음 생성해놓은 인스턴스를 리턴하기 때문에 메모리 낭비를 방지할 수 있다.

 

2. 싱글톤으로 구현한 인스턴스는 전역이다. 즉, 다른 클래스의 인스턴스들이 데이터를 공유할 수 있다.

도메인 관점에서 인스턴스가 한 개만 존재하는 것을 보증하고 싶은 경우 싱글톤 패턴을 사용하기도 한다.

 

 

단점

1. 코드 자체가 너무 길어진다

멀티스레딩 환경에서 발생할 수 있는 동시성 문제를 해결하기 위해 syncronized 키워드가 필요하다

 

2. 테스트하기가 어렵다.

애플리케이션 전역에서 상태를 공유하기 때문에 매번 인스턴스의 상태를 초기화시켜주어야 한다.

 

3. SOLID원칙 중 DIP를 위반하게되고 OCP원칙도 위반할 가능성이 높다.

의존 관계상 클라이언트가 구체 클래스에 의존하게 된다. (new 키워드를 사용한 클래스 내부 객체 생성)

 

4. 자식 클래스를 만들 수 없고 내부 상태를 변경하기 어렵다.

 

싱글톤 패턴은 안티패턴으로 불릴 만큼 단독으로 사용한다면 객체 지향에 위반되는 사례가 많다.
스프링과 같은 프레임워크에서 사용하면 단점을 보완하여 사용할 수 있다.
(스프링 빈은 기본적으로 싱글톤 스코프로 생성되고 관리된다)
@Component
public class SingletonPattern{
//싱글톤 스코프 : 프로그램에서 해당 빈의 인스턴스를 오직 하나만 생성해서 재사용하는것
//상태를 저장하지 않고 로직만 존재하는 경우(매번 객체생성이 불필요하기 때문)
}

 

구현방법

1. static과 synchronized

public class SingletonPattern {
    private volatile static SingletonPattern instance;

    private SingletonPattern() {
    
    }

    public static SingletonPattern getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new SingletonPattern();
                }
            }
        }
        return instance;
    }
}

instance가 아직 초기화되지 않았을 때만 syncronized를 호출해 쓰레드 간 동기화 오버헤드를 줄이는 방법

volatile을 이용해 재배치 문제가 생기지 않게 한다.

volatile ( 자세한 설명 : https://nesoy.github.io/articles/2018-06/Java-volatile )

volatile로 선언된 변수가 있는 코드는 최적화되지 않는다.
Read시 CPU cache에 저장된 값이 아닌 Main Memory에서 읽고, Write 시 Main Memory까지 작성하는 것

 

2. LasyHolder

public class SingletonPattern {
    private SingletonPattern() {
    
    }

    public static SingletonPattern getInstance() {
        return LazyHolder.INSTANCE;
    }

    private static class LazyHolder {
        private static final SingletonPattern INSTANCE = new SingletonPattern();
    }
}

 

 

getInstance 호출 시 LasyHolder.INSTANCE가 참조되면서 LasyHolder가 초기화되며 INSTANCE가 생성된다.

싱글톤 패턴 구현 시 추천되는 방법으로 효율적인 구현 방법이다.

728x90
300x250
mag1c

mag1c

2년차 주니어 개발자.

[Spring] 스프링이란? 스프링의 특징(제어의 역전(IoC), 의존성 주입(DI), 관점지향 프로그래밍(AOP), POJO)

Tech/Java,Kotlin,Spring 2023. 3. 24. 07:12
728x90
728x90

스프링


JAVA의 웹 프레임워크이다.

 

Java 언어를 기반으로 사용한다.

Java로 다양한 어플리케이ㅐ션을 만들기 위한 프로그래밍 틀이다.

Java의 활용도가 높아지면서 JSP, MyBatis, JPA등의 기술이 생겨났다.

Spring은 앞서 말한 기술들을 더 쉽게 사용할 수 있게 해주는 오픈소스 프레임워크이다.

 

 

프레임워크(FrameWork)


어떠한 목적을 달성하기 위해 복잡하게 얽혀있는 문제를 해결하기 위한 구조이며, 개발에 있어 하나의 뼈대 역할을 한다.

 

애플리케이션 개발 시 필수적인 코드, 알고리즘, DB 커넥션 등의 기능들을 위해 뼈대를 제공한다.

구체적인 설계와 구현을 재사용이 가능하게끔 상호 협력하는 클래스와 인터페이스의 집합이다.

 

라이브러리(Library)


라이브러리는 주로 소프트웨어를 개발할 때 컴퓨터 프로그램이 사용하는 비휘발성 자원의 모임이다. 여기에는 구성 데이터, 문서, 도움말, 메시지 틀, 미리 작성된 코드, 함수, 클래스, 값, 자료형 사양 등을 포함할 수 있다.

 

개발에 필요한 것들을 미리 구현해 놓고 필요한 곳에서 호출하여 사용 가능하도록 만들어진 집합이다.

 

 

프레임워크 vs 라이브러리


가장 큰 차이점은 제어 흐름이다.

프레임워크는 제어의 역전(IoC : Inversion of Control)이 적용된다. 프레임워크에게 제어의 흐름을 넘겨 개발자가 작성하는 코드에서 신경 써야 할 부분을 줄일 수 있다.

 

프레임워크는 전체적인 흐름을 쥐고 있으며 애플리케이션 코드는 프레임워크에 의해 사용된다.

애플리케이션 코드는 프레임워크가 짜놓은 틀 안에서 수동적으로 동작하기 때문에 제어의 흐름은 프레임워크에 있다.

 

라이브러리는 개발자가 전체적인 흐름을 만들며 라이브러리를 직접 가져다 쓴다.

라이브러리는 개발자에게 전적으로 제어 흐름이 있어 필요할 때마다 능동적으로 라이브러리를 호출하여 사용한다.

 

 

 

제어의 역전(IoC : Inversion of Control)


개발자가 작성한 객체나 메서드의 생명주기의 제어를 개발자가 아니라 외부에 위임하는 설계원칙

 

객체의 생성, 설정, 초기화, 메서드 호출 및 소멸 등의 객체의 생명주기를 프로그래머가 아닌 프레임워크에게 위임할 수 있다. 즉 외부 라이브러리가 프로그래머가 작성한 코드를 호출하고, 흐름을 제어한다.

 

 

의존성 주입(DI : Dependency Injection)


IoC를 실현하기 위한 여러 디자인 패턴 중 하나로 객체간 결합을 느슨하게 만들어 유연하고 확장성이 뛰어난 코드를 작성하기 위한 패턴이다.

 

프로그램에서 구성 요소의 의존 관계가 소스코드 내부가 아닌 외부의 설정 파일을 통해 정의되는 방식이다.

코드 간의 재사용성을 높이고, 소스코드를 다양한 곳에서 사용하며 모듈 간의 결합도를 낮출 수 있다.

 

 

스프링의 특징


1. IoC(Inversion of Control)

2. DI(Dependency Injection)

 

3. POJO(plain Old Java Object) : Getter, Setter로 구성된 가장 순수한 형태의 기본 클래스

  - 스프링 프레임워크는 일반적인 자바 코드를 이용하여 개발이 가능하다.

 

4. AOP(Aspect Oriented Programming / 관점 지향 프로그래밍)

  - 반복적인 코드를 줄이고 개발자가 비즈니스 로직에만 집중할 수 있도록 지원한다.

 

 

 

POJO, AOP에 대한 이해가 필요하여 따로 포스팅하여 다루도록 하겠다. 포스팅이 완료되면 링크 남겨둘 예정

728x90
300x250
mag1c

mag1c

2년차 주니어 개발자.

Spring MVC 구조 / MVC1 MVC2

Tech/Java,Kotlin,Spring 2023. 2. 5. 21:44
728x90
728x90

MVC (Model - View - Controller)


출처 - 위키백과(MVC)

 

소프트웨어 공학에서 사용되는 소프트웨어 디자인 패턴이다.

Model 모든 데이터 정보를 가공하여 가지고 있는 컴포넌트
상태 변화가 있을 때 컨트롤러와 뷰에 통보한다.
(뷰는 최신 결과를 리턴, 컨트롤러는 적용 가능한 명령을 추가,제거,수정)
View 사용자가 보는 시각적인 UI 요소
Controller Model과 View를 연결 해 주는 역할
Model과 View에 명령을 보낸다.

 

 

MVC-1


View와 Controller을 JSP가 담당한다.

JSP에서 MVC가 모두 이루어져 재사용성, 가독성이 떨어져 유지보수성이 낮다.

 

출처 https://chanhuiseok.github.io/posts/spring-3/

 

 

MVC-2


JSP에서 모든 것을 수행하던 MVC1 패턴과 달리 M, V, C의 역할이 분리되어 있다.

오류 발생 혹은 수정 시 M, V, C 중 해당 요소만 수정할 수 있어 유지보수성이 높다.

 

출처 https://chanhuiseok.github.io/posts/spring-3/

 

 

 

Spring Framework에서의 MVC


MVC가 유기적으로 동작하도록 다양한 요소들이 있다.

구성요소 설명
DispatcherServlet
(FrontController)
- 제일 앞 단에서 HTTP Request를 처리
- 요청 시 요청과 매핑되는 컨트롤러를 지정하는 컨트롤러
Controller
(Handler)
- HTTP Request를 처리하여 Model을 생성하고, View를 지정
- DispatcherServlet에서 지정된 Controller는 요청을 처리하는 과정에서 필요한 데이터를 뽑아 Model에 저장하며, 요청에 따라 보여줄 View Name 및 View를 반환한다.
ModelAndView - Controller에 의해 리턴 된 Model과 View가 저장(Wrapping)된 객체
- Model : Key-Value 형태의 데이터 저장 객체
- View(ViewName) : ViewResolver에 리턴 될 View 지정
ViewResolver ModelAndView 객체를 처리하여 View를 리턴(Model에 저장된 데이터 사용)
사용자가 특정 URL로 들어갔을 때 보여지는 View가 이 곳에서 만들어진다.

 

출처 : https://nickjoit.tistory.com/9

 

728x90
300x250
mag1c

mag1c

2년차 주니어 개발자.

Spring MVC 프로젝트 폴더의 구조

Tech/Java,Kotlin,Spring 2023. 1. 30. 14:45
728x90
728x90

MVC 프로젝트 폴더의 구조


Legacy Project를 생성했을 때의 대략적 구조

 

src/main/java

자바 파일이 모여있는 곳 (Controller, service, vo, dao, dto, mapper, api..)

 

 

src/main/resources

클래스의 리소스들을 보관하는 곳

DB연결을 위한 자원, 의존성 주입을 위한 XML 파일 등 자바 코드와 관련된 모든 것들을 보관한다

 

 

src/test

테스트를 위한 자바 파일 및 리소스를 보관하는 곳

 

 

Maven Dependencies

메이븐에서 자동으로 관리 해 주는 라이브러리 폴더로 pom.xml에 작성된 라이브러리들을 자동으로 다운받아 관리한다.

 

 

src

웹 관련 자원들이 담겨 있는 루트 폴더로 test는 테스트 공간이다.

 

 

src/main/webapp/resources

웹에 필요한 js, css, img 파일 등 다양한 자원을 보관하며, 컨트롤러에서 요청을 가로채지 않고 바로 접근이 가능하도록 설정해서 사용하는 공간

 

 

src/main/webapp/WEB-INF

웹에 필요한 코드파일, 컴파일 된 파일, 여러 환경 설정 파일들이 보관되는 곳

외부 사용자가 직접 접근할 수 없으며 컨트롤러를 통해 내부적 접근만 가능하다.

 

 

src/main/webapp/WEB-INF/views

JSP, HTML 파일이 보관되는 곳이며 해당 폴더는 루트(/)의 기준점이 된다.

사용자가 입력하고 컨트롤러가 받아주는 URL이 이 폴더의 구조를 따라간다.

 

 

src/main/webapp/WEB-INF/classes

컴파일된 파일들이 보관되는 곳

 

 

src/main/webapp/WEB-INF/spring

스프링 환결설정 파일이 보관되는 곳

728x90
300x250
mag1c

mag1c

2년차 주니어 개발자.

Spring MVC Project 셋팅

Tech/Java,Kotlin,Spring 2023. 1. 30. 11:48
728x90
728x90

프로젝트 만들기


1. New - Other에서 legacy검색 후 Spring Legacy Project 생성

 

 

 

2. 프로젝트 이름을 작성하고, Templates - Spring MVC Project 선택 후 Next

 

 

 

3. top-level package를 정의해주면 된다.

 

 

 

 

pom.xml


사용할 버전과 pom.xml에 적혀 있는 버전이 다르다면 맞춰주면 된다.

pom.xml (pom : project object model)

Maven의 빌드 정보를 담고 있는 파일
Maven

자바 프로젝트를 관리하는 툴 (자바 소스를 컴파일하고 패키징해서 deploy까지 자동화 해 준다.)
미리 작성된 xml 파일을 이용하여 라이브러리를 자동으로 다운하거나 프로젝트를 빌드 해 준다.

 

해당 작업까지 완료한 후

1. Project - clean

2. Run As - Maven clean

3. Run As - Maven build

를 차례대로 진행하자.

진행하기전에 다음과 같은 작업을 먼저 수행하자

1. 프로젝트 우클릭 → properties → project Facets
2. Java (Version 1.6) 우클릭 → Change version1.8로 변경
3. Apply and close

 

 

maven 첫 실행 시


Goals - compile 로 설정 후 Run

 

 

 

프로젝트 자바 버전 변경(→1.8)


프로젝트 우클릭 - Properties - Java Build Path에서 자바 버전이 1.8버전이 아니라면 다음과 같은 작업을 수행하자.

 

1. Libraries에서 Edit 클릭

 

2. System library - Execution environment - 1.8로 변경 후 Enviroments 클릭

 

 

3. 1.8을 찾아 적용 후 Apply and close

 

 

Tomcat 설치


9.0 버전의 Tomcat을 사용 할 것이다.

↓↓↓↓다운로드 페이지↓↓↓↓

https://tomcat.apache.org/tomcat-9.0-doc/index.html

 

Apache Tomcat 9 (9.0.71) - Documentation Index

This is the top-level entry point of the documentation bundle for the Apache Tomcat Servlet/JSP container. Apache Tomcat version 9.0 implements the Servlet 4.0 and JavaServer Pages 2.3 specifications from the Java Community Process, and includes many addit

tomcat.apache.org

톰캣(Tomcat)

웹 서버와 연동하여 실행할 수 있는 자바 환경을 제공하여 자바서버 페이지(JSP)와 자바 서블릿이 실행할 수 있는 환경을 제공하는 서블릿 컨테이너이다.

 

 

해당 파일을 다운로드 하여 압축을 해제한 뒤, 폴더 명을 알아보기 쉽게 tomcat9로 변경하였다.

 

Servers 설정


만약 Servers를 찾을 수 없다면

Window - Show view - Servers

Window - Show view - Other - Servers 검색

클릭하여 새 서버를 생성한다

 

 

tomcat을 검색 후 원하는 버전의 tomcat을 선택한 후 Next

 

 

Browse를 선택하여 tomcat 폴더를 선택한 후 Next

(경로에 한글이 있으면 서버 실행 시 계속 에러가 발생한다. 상관은 없지만 눈에 거슬린다.)

 

 

Add 혹은 Add All을 클릭하여 Available에 있는 프로젝트 파일을 Configured로 보낸 후 Finish

 

 

성공적으로 생성되었다.

 

 

위의 사진에 있는 해당 서버를 더블클릭 하자.

Overview

1. 서버 시작에 걸리는 최대 시간 설정이다. 규모가 커질 경우 45초로 해결되지 않을 때도 있다. 우선 변경하지 않겠다.

2. 포트번호 8080의 충돌이 잦아 8081로 변경 해 주었다.

 

Modules

Path에 초기 값이 들어와 있는 모습이다.

페이지의 경로가 localhost:8080/test1/.... 으로 기본값이 test1이라는 뜻이다.

Edit를 눌려 Path값을 초기화 해 주자. (슬래시만 남겨놓으면 된다)

 

 

모든 작업이 완료되었다면 저장을 해야한다 꼭.

 

 

인코딩 필터 설정


브라우저에서 보내는 요청(Request)과 응답(Response)을 모두 UTF-8 로 고정하기 위해 인코딩 필터를 설정한다.

 

src/main/webapp/WEB-INF/web.xml

해당 코드를 </web-app> 위에 작성하면 된다.

<filter>
    <filter-name>encodingFilter</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter
    </filter-class>
    <init-param>
        <param-name>encoding</param-name>
        <param-value>UTF-8</param-value>
    </init-param>
    <init-param>
        <param-name>forceEncoding</param-name>
        <param-value>true</param-value>
    </init-param>
</filter>
<filter-mapping>
    <filter-name>encodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

 

 

로그 레벨 설정


 

src/main/resources/log4j.xml

warn으로 되어있는 value를 debug로 변경

<!-- Root Logger -->
<root>
    <priority value="debug" />
    <appender-ref ref="console" />
</root>

 

 

 

실행 완료


728x90
300x250
mag1c

mag1c

2년차 주니어 개발자.

27. 검색 기능 - 점프 투 스프링부트(게시판 만들기)

Tech/Java,Kotlin,Spring 2023. 1. 7. 10:43
728x90
728x90
해당 게시글은 점프 투 스프링부트 교재를 통한 개인 학습 용도이며 기초 세팅은 생략하였습니다.
자바 8, 스프링부트 2.7.7버전 입니다.

 

 

 

 

 

 

[  검색 기능  ]

Specification

여러 테이블에서 데이터를 검색해야 할 경우에는 JPA가 제공하는 Specification 인터페이스를 사용하는 것이 편리하다. Specification을 어떻게 사용할 수 있는지 예제를 통해서 알아보자.

 

Specification
Specification은 보다 정교한 쿼리의 작성을 도와주는 JPA의 도구이다.
https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#specifications

 

QuestionService에 search 메서드를 추가하고, getList메서드에 키워드(kw)를 추가하자.

(...생략...)
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Join;
import javax.persistence.criteria.JoinType;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import org.springframework.data.jpa.domain.Specification;
import com.example.board.answer.Answer;
(...생략...)
public class QuestionService {

	private final QuestionRepository questionRepository;
	
    private Specification<Question> search(String kw) {
        return new Specification<Question>() {
            private static final long serialVersionUID = 1L;
            @Override
            public Predicate toPredicate(Root<Question> q, CriteriaQuery<?> query, CriteriaBuilder cb) {
                query.distinct(true);  // 중복을 제거 
                Join<Question, SiteUser> u1 = q.join("author", JoinType.LEFT);
                Join<Question, Answer> a = q.join("answerList", JoinType.LEFT);
                Join<Answer, SiteUser> u2 = a.join("author", JoinType.LEFT);
                return cb.or(cb.like(q.get("subject"), "%" + kw + "%"), // 제목 
                        cb.like(q.get("content"), "%" + kw + "%"),      // 내용 
                        cb.like(u1.get("username"), "%" + kw + "%"),    // 질문 작성자 
                        cb.like(a.get("content"), "%" + kw + "%"),      // 답변 내용 
                        cb.like(u2.get("username"), "%" + kw + "%"));   // 답변 작성자 
            }
        };
    }
	
	public Page<Question> getList(int page, String kw){
		List<Sort.Order> sorts = new ArrayList<>();
		sorts.add(Sort.Order.desc("createDate"));
		Pageable pageable = PageRequest.of(page, 10, Sort.by(sorts));
		Specification<Question> spec = search(kw);
		return this.questionRepository.findAll(spec, pageable);
	}
    
    (...생략...)
}

search 메서드는 검색어(kw)를 입력받아 쿼리의 조인문과 where문을 생성하여 리턴하는 메서드이다.

 

q
Root, 즉 기준을 의미하는 Question 엔티티의 객체 (질문 제목과 내용을 검색하기 위해 필요)

u1
Question 엔티티와 SiteUser 엔티티를 아우터 조인하여 만든 SiteUser 엔티티의 객체. Question 엔티티와 SiteUser 엔티티는 author 속성으로 연결되어 있기 때문에 q.join("author")와 같이 조인해야 한다. (질문 작성자를 검색하기 위해 필요)

a
Question 엔티티와 Answer 엔티티를 아우터 조인하여 만든 Answer 엔티티의 객체. Question 엔티티와 Answer 엔티티는 answerList 속성으로 연결되어 있기 때문에 q.join("answerList")와 같이 조인해야 한다. (답변 내용을 검색하기 위해 필요)

u2
바로 위에서 작성한 a 객체와 다시 한번 SiteUser 엔티티와 아우터 조인하여 만든 SiteUser 엔티티의 객체 (답변 작성자를 검색하기 위해서 필요)

 

검색어(kw)가 포함되어 있는지를 like로 검색하기 위해 제목, 내용, 질문 작성자, 답변 내용, 답변 작성자 각각에 cb.like를 사용하고 최종적으로 cb.or로 OR 검색되게 하였다.

 

getList메서드에서는 kw 값으로 Specification 객체를 생성하여 findAll 메서드 호출시 전달하도록 하였다.

 

 

QuestionRepository

Specification을 사용하기 위해서 QuestionRepository를 다음과 같이 수정하자.

package com.example.board.question;

import java.util.List;

import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.jpa.repository.JpaRepository;

public interface QuestionRepository extends JpaRepository<Question, Integer> {
    Question findBySubject(String subject);
    Question findBySubjectAndContent(String subject, String content);
    List<Question> findBySubjectLike(String subject);
    Page<Question> findAll(Pageable pageable);
    Page<Question> findAll(Specification<Question> spec, Pageable pageable);

}

Specification과 Pageable 객체를 입력으로 Question 엔티티를 조회하는 findAll 메서드를 선언했다.

 

 

QuestionController

(... 생략 ...)
public class QuestionController {

    (... 생략 ...)

    @GetMapping("/list")
    public String list(Model model, @RequestParam(value = "page", defaultValue = "0") int page,
            @RequestParam(value = "kw", defaultValue = "") String kw) {
        Page<Question> paging = this.questionService.getList(page, kw);
        model.addAttribute("paging", paging);
        model.addAttribute("kw", kw);
        return "/question/question_list";
    }

    (... 생략 ...)
}

검색어에 해당하는 kw 파라미터를 추가했고 디폴트값으로 빈 문자열을 설정했다.

화면에서 입력한 검색어를 화면에 유지하기 위해 model.addAttribute("kw", kw)로 kw 값을 저장했다.

화면에서 kw 값이 파라미터로 들어오면 해당 값으로 질문 목록이 검색되어 조회될 것이다.

 

 

검색 화면


검색 창

question_list.html

<html layout:decorate="~{layout}">
<div layout:fragment="content" class="container my-3">
    <div class="row my-3">
        <div class="col-6">
            <a th:href="@{/question/create}" class="btn btn-primary">질문 등록하기</a>
        </div>
        <div class="col-6">
            <div class="input-group">
                <input type="text" id="search_kw" class="form-control" th:value="${kw}">
                <button class="btn btn-outline-secondary" type="button" id="btn_search">찾기</button>
            </div>
        </div>
    </div>
    <table class="table">
        (... 생략 ...)
    </table>
    <!-- 페이징처리 시작 -->
    (... 생략 ...)
    <!-- 페이징처리 끝 -->
    <form th:action="@{/question/list}" method="get" id="searchForm">
    <input type="hidden" id="kw" name="kw" th:value="${kw}">
    <input type="hidden" id="page" name="page" th:value="${paging.number}">
</form>
</div>
</html>

<table> 태그 상단 우측에 검색어를 입력할 수 있는 텍스트창을 생성하였다.

맨 밑에 있던 "질문 등록하기" 버튼은 검색 창의 좌측으로 이동했다.

자바 스크립트에서 이 텍스트창에 입력된 값을 읽기 위해 다음처럼 텍스트창 id 속성에 "search_kw"라는 값을 추가했다.

<input type="text" id="search_kw" class="form-control" th:value="${kw}">

 

또한 그리고 page와 kw를 동시에 GET으로 요청할 수 있는 searchForm을 추가했다.

GET 방식으로 요청해야 하므로 method 속성에 "get"을 설정했다.

kw와 page는 이전에 요청했던 값을 기억하고 있어야 하므로 value에 값을 유지할수 있도록 했다.

그리고 action 속성은 "폼이 전송되는 URL"이므로 질문 목록 URL인 /question/list를 지정했다.

 

GET 방식을 사용하는 이유

만약 GET이 아닌 POST 방식으로 검색과 페이징을 처리한다면 웹 브라우저에서 "새로고침" 또는 "뒤로가기"를 했을 때 "만료된 페이지입니다."라는 오류를 종종 만나게 될 것이다. 왜냐하면 POST 방식은 동일한 POST 요청이 발생할 경우 중복 요청을 방지하기 위해 "만료된 페이지입니다." 라는 오류를 발생시키기 때문이다.

 

 

페이징

그리고 기존 페이징을 처리하는 부분도 ?page=1 처럼 직접 URL을 링크하는 방식에서 값을 읽어 폼에 설정할 수 있도록 다음처럼 변경해야 한다. 왜냐하면 검색어가 있을 경우 검색어와 페이지 번호를 함께 전송해야 하기 때문이다.

question_list.html

(... 생략 ...)
<!-- 페이징처리 시작 -->
<div th:if="${!paging.isEmpty()}">
  <ul class="pagination justify-content-center">
    <li class="page-item" th:classappend="${!paging.hasPrevious} ? 'disabled'">
      <a class="page-link" href="javascript:void(0)" th:data-page="${paging.number-1}">
        <span>이전</span>
      </a>
    </li>
    <li th:each="page: ${#numbers.sequence(0, paging.totalPages-1)}"
      th:if="${page >= paging.number-5 and page <= paging.number+5}"
      th:classappend="${page == paging.number} ? 'active'" class="page-item">
      <a th:text="${page}" class="page-link" href="javascript:void(0)" th:data-page="${page}"></a>
    </li>
    <li class="page-item" th:classappend="${!paging.hasNext} ? 'disabled'">
      <a class="page-link" href="javascript:void(0)" th:data-page="${paging.number+1}">
        <span>다음</span>
      </a>
    </li>
  </ul>
</div>
<!-- 페이징처리 끝 -->
(... 생략 ...)

모든 페이지 링크를 href 속성에 직접 입력하는 대신 data-page 속성으로 값을 읽을 수 있도록 변경했다.

 

 

검색 스크립트

question_list.html

(... 생략 ...)
    <!-- 페이징처리 끝 -->
    <form th:action="@{/question/list}" method="get" id="searchForm">
        <input type="hidden" id="kw" name="kw" th:value="${kw}">
        <input type="hidden" id="page" name="page" th:value="${paging.number}">
    </form>
</div>
<script layout:fragment="script" type='text/javascript'>
const page_elements = document.getElementsByClassName("page-link");
Array.from(page_elements).forEach(function(element) {
    element.addEventListener('click', function() {
        document.getElementById('page').value = this.dataset.page;
        document.getElementById('searchForm').submit();
    });
});
const btn_search = document.getElementById("btn_search");
btn_search.addEventListener('click', function() {
    document.getElementById('kw').value = document.getElementById('search_kw').value;
    document.getElementById('page').value = 0;  // 검색버튼을 클릭할 경우 0페이지부터 조회한다.
    document.getElementById('searchForm').submit();
});
</script>
</html>

 

만약 다음과 같이 class 속성값으로 "page-link"라는 값을 가지고 있는 링크를 클릭하면

<a class="page-link" href="javascript:void(0)" th:data-page="${paging.number-1}">
  <span>이전</span>
</a>

이 링크의 data-page 속성값을 읽어 searchForm의 page 필드에 설정하여 searchForm을 요청하도록 다음과 같은 스크립트를 추가했다.

const page_elements = document.getElementsByClassName("page-link");
Array.from(page_elements).forEach(function(element) {
    element.addEventListener('click', function() {
        document.getElementById('page').value = this.dataset.page;
        document.getElementById('searchForm').submit();
    });
});

검색버튼을 클릭하면 검색어 텍스트창에 입력된 값을 searchForm의 kw 필드에 설정하여 searchForm을 요청하도록 다음과 같은 스크립트를 추가했다.

const btn_search = document.getElementById("btn_search");
btn_search.addEventListener('click', function() {
    document.getElementById('kw').value = document.getElementById('search_kw').value;
    document.getElementById('page').value = 0;  // 검색버튼을 클릭할 경우 0페이지부터 조회한다.
    document.getElementById('searchForm').submit();
});

그리고 검색버튼을 클릭하는 경우는 새로운 검색에 해당되므로 page에 항상 0을 설정하여 첫 페이지로 요청하도록 했다.

 

 

위와 같이 해당 문장만 있는 게시물이 조회된다면 성공이다.

 

 

@Query


Specification 대신 직접 쿼리를 작성하여 수행하는 방법에 대해서 알아보자.

QuestionRepository

(... 생략 ...)
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

public interface QuestionRepository extends JpaRepository<Question, Integer> {
    (... 생략 ...)

    @Query("select "
            + "distinct q "
            + "from Question q " 
            + "left outer join SiteUser u1 on q.author=u1 "
            + "left outer join Answer a on a.question=q "
            + "left outer join SiteUser u2 on a.author=u2 "
            + "where "
            + "   q.subject like %:kw% "
            + "   or q.content like %:kw% "
            + "   or u1.username like %:kw% "
            + "   or a.content like %:kw% "
            + "   or u2.username like %:kw% ")
    Page<Question> findAllByKeyword(@Param("kw") String kw, Pageable pageable);
}

@Query를 작성할 때에는 반드시 테이블 기준이 아닌 엔티티 기준으로 작성해야 한다. 즉, site_user와 같은 테이블명 대신 SiteUser처럼 엔티티명을 사용해야 하고 조인문에서 보듯이 q.author_id=u1.id와 같은 컬럼명 대신 q.author=u1처럼 엔티티의 속성명을 사용해야 한다.

 

@Query에 파라미터로 전달할 kw 문자열은 메서드의 매개변수에 @Param("kw")처럼 @Param 애너테이션을 사용해야 한다. 검색어를 의미하는 kw 문자열은 @Query 안에서 :kw로 참조된다.

 

 

작성한 findAllByKeyword 메서드를 사용하기 위해 QuestionService를 다음과 같이 수정하자.

(... 생략 ...)
public class QuestionService {

    (... 생략 ...)

    public Page<Question> getList(int page, String kw) {
        List<Sort.Order> sorts = new ArrayList<>();
        sorts.add(Sort.Order.desc("createDate"));
        Pageable pageable = PageRequest.of(page, 10, Sort.by(sorts));
        return this.questionRepository.findAllByKeyword(kw, pageable);
    }

    (... 생략 ...)
}

 

서비스에 기존 작성했던 Specification을 제거해도 동일하게 동작할 것이다.

 

 

 

 

 

728x90
300x250
mag1c

mag1c

2년차 주니어 개발자.

방명록