JAVA의 컴파일과정

https://steady-snail.tistory.com/67
https://sas-study.tistory.com/262

  • 자바는 OS에 독립적이다.
    • 그 이유는 JVM이 OS와 프로그램의 사이에서 기계어로 해석해주는 역할을 하기 때문.
    • 어떠한 OS든 JAVA가 설치 되어 있다면 JVM에 의해서 .java코드가 기계어로 해석될 수 있다.개발자가 IDE를 통해 .java파일을 생성한다.
  1. Build를 하게되면 Java Compiler(javac)를 통해 .class파일을 생성하게된다.
    • 아직 컴퓨터가 읽을 수 없는 자바 바이트코드.
  2. 자바 바이트코드(.class)는 클래스 로더에게 전달된다.
    1. .class 파일 안에 어떤 필드가 몇개 선언돼 있는지, method는 몇 개고, 이름이 뭔지, 바이트 코드까지 포함해
      클래스에 대한 모든 정보가 들어 있다.
  3. 클래스 로더는 동적로딩을 통해 필요한 클래스들을 로딩 및 링크하여 런타임 데이터 영역 즉, JVM 메모리에 올린다.
    1. 런타임 데이터 영역 중 하나인 Method Area에 올라간다.
  4. JVM메모리에 올라온 바이트코드는 실행엔진에 의해 기계어로 해석되어 실행된다.
    1. JVM의 목적은 바이트코드를 기계어로 번역해서 CPU에게 일을 시키는 것이다.
    2. 실행엔진에 있는 인터프리터가 수행하게 된다.

런타임 데이터 영역

  • JVM은 OS위에서 실행되면서 메모리 영역을 할당 받게되는데 이를 런타임 데이터 영역이라고 한다. 크게 5가지로 세분화 할 수 있다.

출처 :  https://aljjabaegi.tistory.com/387

이 중 PC Register, JVM Stack, Native Method Stack은 스레드 마다 하나씩 생성되고 Heap, Method Aread는 모든 스레드가 공유해서 사용된다. 따라서 멀티쓰레드 프로그래밍을 할 때 동기화에 주의해야하는 영역이다.

  • PC Register : PC 레지스터는 현재 수행 중인 명령의 주소를 가지며 스레드가 시작될 떄 생성되며 각 스레드마다 하나씩 존재한다.
  • JVM 스택 : 스택 프레임이라는 구조체를 저장하는 스택이다. 예외 발생시 printStackTrace() 메서드로 보여주는 Stack Trace의 각 라인 하나가 스택 프레임을 표현한다. JVM 스택 역시 PC레지스터와 마찬가지로 스레드가 시작될 때 생성되며 각 스레드마다 하나씩 존재한다.
  • 네이티브 메서드 스택 : JAVA 외의 언어로 작성된 네이티브 코드를 위한 스택이다. JNI를 통해 호출하는 C/C++ 등의 코드를 수행하기 위한 스택으로, 언어에 맞게 스택이 생성된다.
  • 힙 : 인스턴스 또는 객체를 저장하는 공간으로 가비지 컬렉션 대상이다. JVM 성능 등의 이슈에서 가장 많이 언급되는 공간이다. 힙 구성 방식이나 가비지 컬렉션 방법 등은 JVM 벤더들의 재량이다.
  • 메서드 영역 : 모든 스레드가 공유하는 영역으로 JVM이 시작될 때 생성된다. JVM이 읽어 들인 각각의 클래스와 인터페이스에 대한 런타임 상수 풀, 필드와 메서드에 대한 정보, Static 변수, 메서드의 바이트 코드 등을 보관한다.
  • 런타임 상수 풀 : JVM 동작에서 가장 핵심적인 역할을 수행하는 곳으로 JVM 명세에서도 따로 중요하게 기술한다. 각 클래스와 인터페이스의 상수 뿐만 아니라, 메서드와 필드에 대한 모든 레퍼런스까지 담고 있는 테이블로 어떤 메서드나 필드를 참조할 때 JVM은 런타임 상수 풀을 통해 해당 메서드나 필드의 실제 메모리상 주소를 찾아서 참조한다.

실행 엔진(Excution Engine)

출처 :  https://aljjabaegi.tistory.com/387

  • 실행 엔진은 클래스 로더를 통해 런타임 데이터 영역에 배친된 바이트 코드를 명령어 단위로 읽어서 실행한다.
  • 바이트 코드의 각 명령어는 1바이트 크기의 OpCode와 추가 피연산자로 이루어져 있다.
  • 이 수행 과정에서 실행 엔진은 바이트 크드를 기계가 실행할 수 있는 형태로 변경하는데 다음 두 가지 방식으로 변경한다.
    • 인터프리터 : 바이트 코드 명령어를 하나씩 읽어서 해석하고 실행한다. 하나하나 해석은 빠르지만 전체적인 실행 속도는 느리다는 단점이 있다. JVM안에서 바이트코드는 기본적으로 인터프리터 방식으로 동작한다.
    • JIT 컴파일러 : 인터프리터의 단점을 보완하기 위해 도입된 방식으로 바이트 코드 전체를 컴파일하여 네이티브 코드로 변경하고 이후에는 해당 메서드를 더 이상 인터프리팅 하지 않고 네이티브 코드로 직접 실행하는 방식이다. 하나씩 인터프리팅하여 실행하는 것이 아니라 바이트 코드 전체가 컴파일된 네이티브 코드를 실행하는 것이기 때문에 전체적인 실행 속도는 인터프리팅 방식보다 빠르다.

'Computer science > JAVA' 카테고리의 다른 글

String Class  (0) 2023.01.01
Serialization(직렬화)  (0) 2022.12.25
오토 박싱 & 오토 언박싱  (0) 2022.12.25
Primitive type & Reference type  (0) 2022.12.24
Call by value와 Call by reference  (0) 2022.12.24

서론

  • 흔히 프로그래밍할 때 쓰는 React.js, Vue.js, Spring 등 라이브러리나 프레임워크의 기본이 되는 디자인 패턴을 공부하려고 합니다.
  • 라이브러리
    • 공통으로 사용될 수 있는 특정한 기능들을 모듈화 한 것을 의미한다. 폴더명, 파일명에 대한 규칙이 없고 프레임워크에 비해 자유 롭다. 예를들어 무언가를 자를때 가위를 사용해서 내가 직접 자른다. 라이브러리는 이와 비슷한 맥락이다.
  • 프레임워크
    • 공통으로 사용될 수 있는 특정한 기능들을 모듈화 한 것을 의미한다. 폴더명, 파일명에 대한 규칙이 있고 라이브러리에 비해 엄격하다. 예를들어 다른 곳을 이동할 때 도구인 비행기를 타고 이동하지만 비행기가 컨트롤하고 나는 단지 가만히 앉아있기만 해야한다. 프레임워크는 이와 비슷한 맥락이다.
  • 디자인 패턴
    • 디자인 패턴이란 프로그램을 설계할 때 발생했던 문제점들을 객체 간의 상호 관계 등을 이용하여 해결할 수 있도록 하나의 '규약'형태로 만들어 놓은 것을 의미한다.

싱글톤 패턴

싱글톤 패턴이란 하나의 클래스에 오직 하나의 인스턴스만을 가지는 패턴이다.

  • 하나의 클래스를 기반으로 여러 개의 개별적인 인스턴스를 만들 수 있지만, 그렇게 하지 않고 하나의 클래스를 기반으로 단 하나의 인스턴스를 만들어 이를 기반으로 로직을 만드는데 쓰이며, 보통 데이터베이스 연결 모듈에 많이 사용한다.
    하나의 인스턴스를 만들어 놓고 해당 인스턴스를 다른 모듈들이 공유하며 사용하기 때문에 인스턴스를 생성할 때 드는 비용이 줄어드는 장점이 있다. 하지만 의존성이 높아진다는 단점이 있다.

    싱글톤 패턴의 단점

  • 싱글톤 패턴은 TDD를 할 때 걸림돌이 된다. TDD를 할 때 단위테스트를 주로 하는데, 단위 테스트는 테스트가 서로 독립적이어야 하며 테스트를 어떤 순서로든 실행할 수 있어야 한다.
    하지만 싱글톤 패턴은 미리 생성된 하나의 인스턴스를 기반으로 구현하는 패턴이므로 각 테스트마다 '독립적인' 인스턴스를 만들기가 어렵다.

    의존성 주입

  • 싱글톤 패턴은 사용하기가 쉽고 굉장히 실용적이지만 모듈 간의 결합을 강하게 만들 수 있다는 단점이 있다. 이 때 의존성 주입을 통해 모듈간의 결합을 조금 더 느슨하게 만들어 해결할 수 있다.

    의존성 주입의 장점

  • 모듈들을 쉽게 교체할 수 있는 구조가 되어 테스팅하기 쉽고 마이그레이션하기도 수월하다. 또한 구현할 때 추상화 레이어를 넣고 이를 기반으로 구현체를 넣어 주기 때문에 애플리케이션 의존성 방향이 일관되고, 애플리케이션을 쉽게 추론할 수 있으며, 모듈간의 관계들이 조금 더 명확해진다.

    의존성 주입의 단점

  • 모듈들이 더욱더 분리되므로 클래스 수가 늘어나 복잡성이 증가될 수 있으며 약간의 런타임 패털티가 생기기도 한다.의존성 주입 원칙
  • 의존성 주입은 상위 모듈은 하위 모듈에서 어떠한 것도 가져오지 않아야 한다. 또한 둘 다 추상화에 의존해야 하며, 이 때 추상화는 세부 사항에 의존하지 말아야 한다는 의존성 주입 원칙을 지켜주면서 만들어야 한다.

'Computer science > 디자인패턴 및 프로그래밍 패러다임' 카테고리의 다른 글

캡슐화  (0) 2023.06.30
SOLID 원칙  (0) 2023.06.30
상속과 합성  (0) 2023.06.30
객체지향과 절차지향  (0) 2023.06.30

입력으로 It is time to study 라는 문자열이 들어오면

가장 긴 단어를 출력하는 유형 

 

 split()

String[] s = str.split(" ");

매개변수로 기준을 정해준다. 지금은 띄어쓰기를 기준으로 한다.

int m = Integer.MIN_VALUE;
for(String x : s){
	int len = x.length();
    if(len>m){
    	m = len;
        answer = x;
        }
}

m은 최소값으로 설정해두고

for each 문을 이용하여 탐색한다. 

for each문을 쓸 수 있는 경우는 부모가 Collection인 경우에 가능하다.

(Set, List, 등등 Map은 아니지만 keySet()같은 함수를 쓴다면 가능)

지금은 String배열이기 때문에 각각 요소를 length함수를 써서 길이를 구하고

m보다 크면 m을 len으로 초기화하고 그때 요소를 answer에 담는다.

 

indexof()

파라미터로 들어온 문자의 인덱스를 반환한다.(첫 번째로 만난) 

못찾은 경우 -1 반환

두번째 파라미터에 시작위치를 지정할 수 있다.

 

substring()

문자열을 자를때 쓰는 함수이다.

파라미터로

(시작위치)

(시작위치, 끝위치)

정할 수 있다. 끝위치 -1 까지의 값이 반환된다.

'알고리즘 with 자바 > 알고리즘' 카테고리의 다른 글

union-find  (1) 2025.06.12
순열  (0) 2021.09.15
멱집합  (0) 2021.09.13
Recursion의 응용 : n queen problem  (0) 2021.09.09
Recursion의 응용 : Counting Cells in a Bob  (0) 2021.09.09

상품을 저장하고 상품 상세 화면으로 리다이렉트 한 것 까진 좋았다.

그런데 고객 입장에서 저장이 잘 된 것인지 아닌지 확신이 들지 않는다. 그래서 저장이 되었다면

저장되었습니다. 라는 메시지를 보여주는 요구사항이 왔다는 가정을 하고 해결해보았다.

 

    @PostMapping("/add")
    public String addItemV6(Item item, RedirectAttributes redirectAttributes){
       Item savedItem = itemRepository.save(item);
       redirectAttributes.addAttribute("itemId",savedItem.getId());
       redirectAttributes.addAttribute("status",true);

        return "redirect:/basic/items/{itemId}";
    }

리다이렉트 할 때 간단히 status = true를 추가하여 뷰 템플릿에서 이 값이 있으면 저장 완료라는 문구를 출력해보았다.

 

RedirectAttributes

RedirectAttributes를 사용하면 URL 인코딩도 해주고, pathVariable, 쿼리 파라미터까지 처리해준다.

 

redirect:/basic/items/{itemId}

pathVariable 바인딩 : {itemId}

나머지는 쿼리 파라미터로 처리 : ?status=true

'웹프로그래밍 > 스프링 MVC' 카테고리의 다른 글

60. PRG Post/Redirect/Get  (0) 2022.04.20
59. 상품 수정  (0) 2022.04.19
58. 상품 등록 처리 - @ModelAttribute  (0) 2022.04.19
57. 상품 등록 폼  (0) 2022.04.18
56. 상품 상세  (0) 2022.04.18

사실 지금까지 진행한 상품 등록 처리 컨트롤러는 심각한 문제가 있다.

상품 등록을 완료하고 웹 브라우저의 새로고침 버튼을 클릭해보면 상품이 계속해서 중복 등록되는 것을 확인할 수 있다.

 

웹 브라우저의 새로 고침은 마지막에 서버에 전송한 데이터를 다시 전송한다.

상품 등록 폼에서 데이터를 입력하고 저장을 선택하면 POST /add +  상품 데이터를 서버로 전송한다.

이상태에서 새로고침을 또 선택하면 마지막에 전송한 POST /add + 상품 데이터를 서버로 다시 전송한다.

그래서 내용은 같고, ID만 다른 상품 데이터가 계속 쌓이는 현상이 생긴다.

 

이 문제를 어떻게 해결해야 할까?

 

새로고침 문제를 해결하려면 상품 저장 후에 뷰 템플릿으로 이동하는 것이 아니라, 상품 상세 화면으로 리다이렉트를 호출해주면 된다.

웹 브라우저는 리다이렉트의 영향으로 상품 저장 후에 실제 상품 상세 화면으로 다시 이동한다. 따라서

마지막에 호출한 내용이 상품 상세 화면인 GET/ items/{id}가 된다.

이후 새로고침을 해도 상품 상세 화면으로 이동하게 되므로 새로고침 문제를 해결할 수 잇다.

 

    @PostMapping("/add")
    public String addItemV5(Item item){
        itemRepository.save(item);
        return "redirect:/basic/items/"+item.getId();
    }

상품 등록 처리 이후에 뷰 템플릿이 아니라 삼품 상세 화면으로 리다이렉트 하도록 코드를 작성해보았다.

 

이런 문제 해결 방식을 PRG Post/Redirect/GET 이라고 한다.

'웹프로그래밍 > 스프링 MVC' 카테고리의 다른 글

61. RedirectAttributes  (0) 2022.04.20
59. 상품 수정  (0) 2022.04.19
58. 상품 등록 처리 - @ModelAttribute  (0) 2022.04.19
57. 상품 등록 폼  (0) 2022.04.18
56. 상품 상세  (0) 2022.04.18

상품 수정 폼 컨트롤러

    @GetMapping("/{itemId}/edit")
    public String editForm(@PathVariable Long itemId, Model model){
        Item item = itemRepository.findById(itemId);
        model.addAttribute("item",item);
        return "basic/editForm";
    }

수정에 필요한 정보를 조회하고, 수정용 폼 뷰를 호출한다.

 

템플릿을 수정 후

 

상품 수정 개발을 하였다.

    @PostMapping("/{itemId}/edit")
    public String edit(@PathVariable Long itemId, @ModelAttribute Item item){
        itemRepository.update(itemId,item);
        return "redirect:/basic/items/{itemId}";
    }

상품 수정은 상품 등록과 전체 프로세스가 유사하다.

GET /items/{itemId}/edit : 상품 수정 폼

POST /items/{itemId}/edit : 상품 수정 처리

 

리다이렉트

상품 수정은 마지막에 뷰 템플릿을 호출하는 대신에 상품 상세 화면으로 이동하도록 리다이렉트를 사용한다.

스프링은 redirect:/... 으로 편리하게 리다이렉트를 지원한다.

redirect:/basic/items/{itemId}

 컨트롤러에 매핑된 @PathVariable의 값은 ridirect에도 사용 할 수 있다.

'웹프로그래밍 > 스프링 MVC' 카테고리의 다른 글

61. RedirectAttributes  (0) 2022.04.20
60. PRG Post/Redirect/Get  (0) 2022.04.20
58. 상품 등록 처리 - @ModelAttribute  (0) 2022.04.19
57. 상품 등록 폼  (0) 2022.04.18
56. 상품 상세  (0) 2022.04.18

이제 상품 등록 폼에서 전달된 데이터로 실제 상품을 등록 처리해보았다.

 

POST - HTML Form

content-type : application/x-www-form-urlencoded

메시지 바디에 쿼리 파라미터 형식으로 전달 

- itemName=itemA&price=10000&quantity=10

예) 회원가입, 상품 주문, HTML Form사용

 

요청 파라미터 형식을 처리해야하므로 @RequestParam을 사용하였다.

@PostMapping("/add")
    public String addItemv1(@RequestParam String itemName,
                       @RequestParam int price,
                       @RequestParam Integer quantity,
                       Model model){
        Item item = new Item();
        item.setItemName(itemName);
        item.setPrice(price);
        item.setQuantity(quantity);

        itemRepository.save(item);

        model.addAttribute("item",item);

        return "basic/item";
    }

@RequestParam String itemName : itemName 요청 파라미터 데이터를 해당 변수에 받는다.

Item 객체를 생성하고 itemRepository를 통해서 저장한다.

저장된 item을 모델에 담아서 뷰에 전달한다.

 

이번에는 @ModelAttribute를 사용해서 한번에 처리하는 방식을 써보았다.

@PostMapping("/add")
    public String addItemV2(@ModelAttribute("item") Item item, Model model){

        itemRepository.save(item);
        // model.addAttribute("item",item); 자동으로 추가되기 때문에 생략가

        return "basic/item";
    }

@ModelAttribute - 요청 파라미터 처리

@ModelAttribute는 Item객체를 생성하고, 요청 파라미터의 값을 프로퍼티 접근법으로 입력해준다.

 

@ModelAttribute - Model 추가

@ModelAttribute는 중요한 한가지 기능이 더 있다. 바로 모델에 @ModelAttribute로 지정된 객체를 자동으로 넣어준다.

코드를 보면 mdoel.addAtrribute("item",item)가 주석처리 되어있다. 그래도 잘 동작한다.

 

모델에 데이터를 담을 때는 이름이 필요하다. 이름은 @ModelAttribute에 지정한 name(value) 속성을 사용한다

만약 다음과 같이 @ModelAttribute의 이름을 다르게 지정하면 다른 이름으로 모델에 포함된다.

 

@ModelAttribute("hello") Item item --->> 이름을 hello 로 지정
model.addAttribute("hello", item); --->> 모델에 hello 이름으로 저장

ModelAttribute 이름을 생략할 수 도 있다.

    @PostMapping("/add")
    public String addItemV3(@ModelAttribute Item item){

        itemRepository.save(item);
        return "basic/item";
    }

 

@ModelAttribute의 이름을 생략하면 모델에 저장될 때 클래스 명의 첫글자를 소문자로 변경해서 등록한다.

 

ModelAttribute 전체를 생략할 수 도 있다.

    @PostMapping("/add")
    public String addItemV4(Item item){
        itemRepository.save(item);
        return "basic/item";
    }

대상 객체는 모델에 자동 등록된다. 이 방법은 생략의 극단적인 모습을 보여주는 방법이라고 생각하는데 적당한 생략이 보기에 직관적일 수 있다고 생각한다. 이 방법은 아는 사람이 봐도 헷갈릴 수도 잇을 것 같다.

'웹프로그래밍 > 스프링 MVC' 카테고리의 다른 글

60. PRG Post/Redirect/Get  (0) 2022.04.20
59. 상품 수정  (0) 2022.04.19
57. 상품 등록 폼  (0) 2022.04.18
56. 상품 상세  (0) 2022.04.18
55. 상품 목록 - 타임 리프  (0) 2022.04.16

상품 등록 폼

같은 URL이지만 HTTP 메서드로 두 기능을 구분한다.

등록 폼은 GET

등록 처리는 POST

    @GetMapping("/add")
    public String addForm(){
        return "basic/addForm";
    }

    @PostMapping("/add")
    public String save(){
        return "basic/addForm";
    }
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="utf-8">
    <link th:href="@{/css/bootstrap.min.css}"
          href="../css/bootstrap.min.css" rel="stylesheet">
</head>
<body>

<div class="container">

    <div class="py-5 text-center">
        <h2>상품 등록 폼</h2> </div>

    <h4 class="mb-3">상품 입력</h4>

    <form action="item.html" th:action method="post"> <div>
        <label for="itemName">상품명</label>
        <input type="text" id="itemName" name="itemName" class="form-
control" placeholder="이름을 입력하세요"> </div>
        <div>
            <label for="price">가격</label>
            <input type="text" id="price" name="price" class="form-control" placeholder="가격을 입력하세요">
        </div> <div>
            <label for="quantity">수량</label>
            <input type="text" id="quantity" name="quantity" class="form-
control" placeholder="수량을 입력하세요"> </div>
        <hr class="my-4">
        <div class="row">
            <div class="col">
                <button class="w-100 btn btn-primary btn-lg" type="submit">상품 등록</button> </div>
            <div class="col">
                <button class="w-100 btn btn-secondary btn-lg"
                        onclick="location.href='items.html'"
                        th:onclick="|location.href='@{/basic/items}'|"
                        type="button">취소</button> </div>
        </div>
    </form>
</div> <!-- /container --> </body>
</html>

속성 변경 - th:action

HTML form에서 action에 값이 없으면 현재 URL에 데이터를 전송한다.

상품 등록 폼의  URL과 실제 상품 등록을 처리하는 URL을 같게 만들고 HTTP로 두 기능을 구분한다.

이렇게 하면 하나의URL로 등록 폼과, 등록 처리를 깔끔하게 처리가 가능하다.

 

취소 시 상품목록으로 이동한다.

'웹프로그래밍 > 스프링 MVC' 카테고리의 다른 글

59. 상품 수정  (0) 2022.04.19
58. 상품 등록 처리 - @ModelAttribute  (0) 2022.04.19
56. 상품 상세  (0) 2022.04.18
55. 상품 목록 - 타임 리프  (0) 2022.04.16
54. 상품 서비스 HTML  (0) 2022.04.15

상품 상세 컨트롤러와 뷰를 만들어보았다.

 

@GetMapping("/{itemId}")
    public String item(@PathVariable long itemId, Model model){
        Item item = itemRepository.findById(itemId);
        model.addAttribute("item",item);
        return "basic/item";
    }

pathVariable 로 넘어온 상품ID로 상품을 조회하고, 모델에 담아둔다. 그리고 뷰 템플릿을 호출한다.

 

상품 상세 뷰

정적 HTML을 뷰 템플릿 영역으로 복사 후 수정하였다.

<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="utf-8">
    <link th:href="@{/css/bootstrap.min.css}"
            href="../css/bootstrap.min.css" rel="stylesheet"> <style>

 .container { max-width: 560px;
} </style>
</head>
<body>
<div class="container">
    <div class="py-5 text-center">
        <h2>상품 상세</h2> </div>
    <div>
        <label for="itemId">상품 ID</label>
        <input type="text" id="itemId" name="itemId" class="form-control" value="1"  th:value="${item.id}" readonly>
    </div> <div>
    <label for="itemName">상품명</label>
    <input type="text" id="itemName" name="itemName" class="form-control"
           value="상품A"  th:value="${item.itemName}" readonly> </div>
    <div>
        <label for="price">가격</label>
        <input type="text" id="price" name="price" class="form-control" value="10000"  th:value="${item.price}"  readonly>
    </div> <div>
    <label for="quantity">수량</label>
    <input type="text" id="quantity" name="quantity" class="form-control" value="10"  th:value="${item.quantity}"readonly>
</div>
    <hr class="my-4">
    <div class="row">
        <div class="col">
            <button class="w-100 btn btn-primary btn-lg"

                    onclick="location.href='editForm.html'"
                    th:onclick="|location.href='@{/basic/items/{itemId}/edit(itemId=${item.id})}'|"
                    type="button">상품 수정</button> </div>
        <div class="col">
            <button class="w-100 btn btn-secondary btn-lg"
                    onclick="location.href='items.html'"
                    th:onclick="|location.href='@{/basic/items}'|"

                    type="button">목록으로</button> </div>
    </div>
</div> <!-- /container --> </body>
</html>

속성 변경

모델에 있는 item정보를 획득하고 프로퍼티 접근법으로 출력한다.(item.getId())

value속성을 th:value 속성으로 변경한다.

 

상품수정 링크

th:onclick="|location.href='@{/basic/items/{itemId}/edit(itemId=${item.id})}'|"

 

목록으로 링크

th:onclick="|location.href='@{/basic/items}'|"

'웹프로그래밍 > 스프링 MVC' 카테고리의 다른 글

58. 상품 등록 처리 - @ModelAttribute  (0) 2022.04.19
57. 상품 등록 폼  (0) 2022.04.18
55. 상품 목록 - 타임 리프  (0) 2022.04.16
54. 상품 서비스 HTML  (0) 2022.04.15
53. 상품 도메인 개발  (0) 2022.04.15

본격적으로 컨트롤러와 뷰 템플릿을 개발해보았다.

 

package hello.itemservice.web.basic;

import hello.itemservice.domain.item.Item;
import hello.itemservice.domain.item.ItemRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;

import javax.annotation.PostConstruct;
import java.util.List;

@Controller
@RequestMapping("/basic/items")
@RequiredArgsConstructor
public class BasicItemController {

    private final ItemRepository itemRepository;

    @GetMapping
    public String items(Model model){
        List<Item> items = itemRepository.findAll();
        model.addAttribute("items",items);
        return "basic/items";
    }

    //테스트 용 데이터
    @PostConstruct
    public void init(){
        itemRepository.save(new Item("itemA",10000,10));
        itemRepository.save(new Item("itemB",20000,20));

    }

}

컨트롤러 로직은 itemRepository에서 모든 상품을 조회한 다음에 모델에 담는다. 그리고 뷰 템플릿을 호출한다.

@RequiredArgsConstructor는 final이 붙은 멤버변수를 사용해서 생성자를 자동으로 만들어준다. 생성자가

하나뿐이라면 스프링이 의존관계를 주입해준다. 

 

테스트용 데이터를 추가 하지않으면 정상 동작하는지 확인하기 어렵다.

@PostConstruct : 해당 빈의 의존관계가 모두 주입되고 나면 초기화 용도로 호출된다.

<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="utf-8">
    <link th:href="@{/css/bootstrap.min.css}"
            href="../css/bootstrap.min.css" rel="stylesheet">
</head>
<body>

<div class="container" style="max-width: 600px">
    <div class="py-5 text-center">
      <h2>상품 목록</h2>
</div>

    <div class="row">
        <div class="col">
            <button class="btn btn-primary float-end"
                    onclick="location.href='addForm.html'"
                    th:onclick="|location.href='@{/basic/items/add}'|"
                    type="button">상품 등록</button>
        </div>
    </div>
    <hr class="my-4"> <div>
        <table class="table">
            <thead>
            <tr>
                <th>ID</th> <th>상품명</th> <th>가격</th> <th>수량</th>
            </tr>
            </thead>
            <tbody>
            <tr th:each="item : ${items}">
                <td><a href="item.html"th:href="@{/basic/items/{itemId}(itemId=${item.id})}" th:text="${item.id}">회원id</a></td>
                <td><a href="item.html"th:href="@{/basic/items/{itemId}(itemId=${item.id})}" th:text="${item.itemName}">상품명</a></td>
                <td th:text="${item.price}">10000</td>
                <td th:text="${item.quantity}">10</td>
            </tr>
            </tbody>
        </table>
    </div>
</div> <!-- /container -->
</body>
</html>

기존에 있던 Html파일을 복사하여 templates하위에 붙여넣고 수정해주었다.(타임리프 문법 사용)

타임리프 선언

<html xmlns:th="http://www.thymeleaf.org">

 

속성 변경 

th:href="@{/css/bootstrap.min.css}"

href="value1"을 th:href="value2"의 값으로 변경한다.

타임리프 뷰 템플릿을 거치게 되면 원래 값을 th:xxx 값으로 변경한다. 만약 값이 없다면 새로 생성한다.

HTML을 그대로 볼 때는 href속성이 사용되고, 뷰 템플릿을 거치게 되면 th:href의 값이 href로 대체 되며 동적으로 변경가능하다.

 

타임리프 핵심

핵심은 th:xxx가 붙은 부분은 서버사이드에서 랜더링 되고 기존 것을 대체한다. th태그가 없으면 기존 속성이 그대로 사용된다.

HTML을 파일로 직접 열었을때, th:xxx가 있어도 웹 브라우저는 th: 속성을 알지 못하므로 무시한다.

따라서 HTML을 파일 보기를 유지하면서 템플릿 기능도 할 수 있다.

 

URL 링크 표현식 - @{}

th:href="@{/css/bootstrap.min.css}"

@{} : 타임리프는 URL 링크를 사용하는 경우 @{}를 사용한다. 이것을 URL 링크 표현식이라 한다.

URL 링크 표현식을 사용하면 서블릿 컨텍스트를 자동으로 포함한다.

 

상품 등록 폼으로 이동

속성 변경 - th:onclick

onclick="location.href='addForm.html'"

th:onclick="|location.href='@{/basic/items/add}'|"

 

리터럴 대체 =|...|

타임리프에서 문자와 표현식 등은 분리되어 있기 때문에 더해서 사용해야 한다.

<span th:text="'Hello, '+${user.name} + '!'">

리터럴 대체 문법을 사용하면?

<span th:text="|Hello,${user.name}!|">

 

결과를 다음과 같이 만들어야한다고 가정하면

location.href='/basic/items/add'

그냥 사용할땐?

th:onclick=" 'location.href='+'\' '+ @{/basic/items/add} + '\' '"

리터럴 대체 문법 사용하면

th:onclick="|location.href='@{/basic/items/add}'|"

 

반복 출력 - th:each

<tr th:each="item : ${items}">

반복은 th:each를 사용한다. 이렇게 하면 모델에 포함된 items 컬렉션 데이터가 item변수에 하나씩 포함되고,

반복문 안에서 item 변수를 사용할 수 있다.

컬렉션의 수 만큼<tr>..</tr>이 하위 테크를 포함해서 생성된다.

 

변수 표현식 - ${...}
<td th:text="${item.price}">10000</td>
모델에 포함된 값이나, 타임리프 변수로 선언한 값을 조회할 수 있다.
프로퍼티 접근법을 사용한다.

 

내용 변경 - th:text
<td th:text="${item.price}">10000</td>

내용의 값을 th:text 의 값으로 변경한다.
여기서는 10000${item.price} 의 값으로 변경한다.

 

URL 링크 표현식2 - @{...}
th:href="@{/basic/items/{itemId}(itemId=${item.id})}"
상품 ID를 선택하는 링크를 확인해보았다.
URL 링크 표현식을 사용하면 경로를 템플릿처럼 편리하게 사용할 수 있다.
경로 변수뿐만 아니라 쿼리 파라미터도 생성한다.

 

타임리프는 순수 HTML을 파일을 웹 브라우저에서 열어도 내용을 확인할 수 있고, 서버를 통해 뷰 템플릿을거치면 동적으로 변경된 결과를 확인할 수 있다. JSP는 웹 브라우저에서 그냥 열면 코드들이 섞여서 정상적인 확인이 불가능하다.
오직 서버를 통해서 JSP를 열어야 한다.
이렇게 순수 HTML을 그대로 유지하면서 뷰 템플릿도 사용할 수 있는 타임리프의 특징을 내추럴 템플릿이라 한다.

'웹프로그래밍 > 스프링 MVC' 카테고리의 다른 글

57. 상품 등록 폼  (0) 2022.04.18
56. 상품 상세  (0) 2022.04.18
54. 상품 서비스 HTML  (0) 2022.04.15
53. 상품 도메인 개발  (0) 2022.04.15
52. 요구사항 분석  (0) 2022.04.14

+ Recent posts