Spring Sescurity 란? 

'인증(Authentication)'과 '권한(Authorization)'에 대한 보안 부분을 담당하는 스프링 하위 프레임워크.
  • 많은 보안 관련 옵션들을 제공해주어 개발자가 보안 로직을 하나씩 작성하지 않아도 되는 장점이 있다.

 

 

Authentication 인증 →  접근하는 유저가 누구인지 확인하는 절차 (ex 로그인)
Authorization   허가 → 인증된 상태에서 권한(일반유저인지 관리자인지)을 확인하고 허락하는 절차

 

'인증' 절차의 예 

- 물건을 사기 위해 쇼핑몰에서 로그인을 하였다.

 

'허가' 절차의 예

- 다른 유저가 단 댓글을 삭제시키려고 했으나 권한이 없어 실패하였다.

  모든 유저의 댓글삭제 가능한 권한은 관리자만 가능.

 

 

그래서! 

유저를 인증하는 단계인 인증이 먼저 이루어지고

인증된 유저가 가지는 권한이 맞는지 확인하는 허가가 다음으로 이루어진다.

결론 : 순서는 인증 다음 허가가 이루어짐.  

 

 

 


[기본설정]

스프링 세큐리티 추가하기 

 

 

 

시큐리티를 추가하면 콘솔창에 시큐리티 임시 패스워드가 나온다.

 

 

 

[ 시큐리티 적용중 ]

새로고침 하면 바로 로그인창이 뜬다.

id : user
password : f8b07d29-8730-4185-a96d-bb336d870387

 

 

 

[application properties] 에서 임시 비번설정 가능.

spring.security.user.name=user
spring.security.user.password=1234 

 

 

 

SecurityConfig 클래스 생성

1. 상속(WebSecurityConfigurerAdapter

2. 어노테이션(@EnableWebSecurity , @Controller )

 

 

 

ctrl + space 눌렀을때 configure(HttpSecurity http)로 자동완성하기

 

 

@EnableWebSecurity
@Controller
public class SecurityConfig extends WebSecurityConfigurerAdapter{
//시큐리티 1.인증(로그인) 2.허가(role에 따른 허용가능한 범위)

@Override
protected void configure(HttpSecurity http) throws Exception {
    // 허가(role에 따른 허용가능한 범위)
    http.authorizeHttpRequests()
        //.antMatchers("/category/**").hasAnyRole("USER","ADMIN") //카테고리는 유저,관리자만
        //.antMatchers("/admin/**").hasAnyRole("ADMIN") //관리자폴더는 관리자만
        .antMatchers("/").permitAll(); //누구나 접근
}

 

아직 관리자, 회원이 없으니까 누구나 접근할 수 있도록 .antMatchers("/").permitAll();  만 남겨두기!

http.authorizeHttpRequests()
      .antMatchers("/").permitAll();

 

 

 

 

[ 유저, 관리자 테이블 만들기 ]

 

users테이블

 

admin 테이블

 

 

반응형
LIST

 

  • 테스트 결제 imPort 사이트

 

  • 카카오페이 복붙

https://github.com/iamport/iamport-manual/blob/master/%EC%9D%B8%EC%A6%9D%EA%B2%B0%EC%A0%9C/sample/kakao.md

 

GitHub - iamport/iamport-manual: 아임포트(iamport) 결제연동을 위한 매뉴얼입니다.

아임포트(iamport) 결제연동을 위한 매뉴얼입니다. Contribute to iamport/iamport-manual development by creating an account on GitHub.

github.com

 

 

cart.html 
<script type="text/javascript" src="https://cdn.iamport.kr/js/iamport.payment-1.2.0.js"></script>​
<script>
  $(function () {
    $('a.checkout').click(function (e) {
      e.preventDefault(); // a태그 이동 정지
      $.get('/cart/clear', {}, function () {}); //카트 클리어 요청
      //$("form#paypalform").submit();
      kakaoPay();
    });
  });
  function kakaoPay() {
    var IMP = window.IMP; // 생략 가능
    IMP.init('가맹점식별코드'); // 예: imp00000000
    IMP.request_pay(
      {
        pg: 'kakaopay',
        //pay_method : 'card', //생략 가능
        merchant_uid: 'order_no_0001', // 상점에서 관리하는 주문 번호
        name: '주문명:결제테스트',
        amount: '[[${ctotal}]]',
        buyer_email: 'iamport@siot.do',
        buyer_name: '구매자이름',
        buyer_tel: '010-1234-5678',
        buyer_addr: '서울특별시 강남구 삼성동',
        buyer_postcode: '123-456',
      },
      function (rsp) {
        if (rsp.success) {
          let msg = '결제가 완료되었습니다.';
          msg += '고유ID : ' + rsp.imp_uid;
          msg += '상점 거래ID : ' + rsp.merchant_uid;
          msg += '결제 금액 : ' + rsp.paid_amount;
          msg += '카드 승인번호 : ' + rsp.apply_num;
          if (!alert(msg)) location.reload();
        } else {
          let msg = '결제에 실패하였습니다.';
          msg += '에러내용 : ' + rsp.error_msg;
          alert(msg);
        }
      }
    );
  }
</script>
☆ 주문할때마다 오더 넘버 다르게해야 결제가 됨
merchant_uid: 'order_no_0001' 

 

 

 

- import 사이트에서 관리자콘솔에 들어가 로그인하기

 

가맹점 식별코드 복붙해서 붙이기

IMP.init('가맹점식별코드');    // 예: imp00000000 

 

 

 

 

- 카카오페이 테스트모드로 설정해야 실제로 결제가 안됨!

 

 

 

- 체크아웃 버튼 눌렀을때

카카오페이 결제

 

 

결제완료

 

 

 

 

- import 사이트의 결제승인내역 확인가능

 

 

 

 

결제후 결제성공 페이지 payment_success.html 로 넘어가는법은..??

반응형
LIST
 $this.parent().parent().find('div.productAdded').fadeIn();
 $this.parent().parent().find('div.productAdded').fadeIn();

카트 담은거 보여주는 [ cart_partial.html ]

-> (장바구니 페이지 제외) 모든 페이지마다 공통으로 들어가는 사이드 카트이다. (상품개수, 총가격)
 

<div class="bg-dark text-white p-3 mt-3" th:fragment="cart_partial">
    <div class="cart cartActive" th:if="${cartActive}">
        <p>장바구니에 <span th:text="${csize}"></span> 개 상품이 있습니다.</p>
        <p>총 가격은 <span th:text="${ctotal}"></span> 원 입니다.</p>
        <p>
            <a href="/cart/view" class="btn btn-success">카트 보기</a>
            <a href="/cart/clear" class="btn btn-danger float-right">비우기</a>
        </p>
    </div>
    <div class="cart cartInactive" th:unless="${cartActive}">
        <p>장바구니가 없습니다.</p>
    </div>
</div>

 

th:if  는 if 절을 의미 , th:unless 는 not if 를 의미

th:if="${cartActive}" 
th:unless="${cartActive}"

[ Common ] 
model.addAttribute("cartActive", cartActive); 을 쓴 이유 
장바구니가 있으면 true , 없으면 false
 
 

th:fragment="cart_partial" -> [categoreis.html] 에 추가

<div th:replace="/fragments/cart_partial"></div> 

 

 


 

[ CatrController ]

 

@GetMapping("/add/{id}") 상품 id 추가할때

 

밑에 추가하기

HashMap<Integer, Cart> cart = (HashMap<Integer, Cart>)session.getAttribute("cart");

int size = 0; //장바구니 상품 갯수
int totalPrice = 0; //총 가격

//장바구니 cart객체 반복 (1,cart객체) (2,cart객체)... id빼고 객체만
for(Cart item : cart.values()) {
    size += item.getQuantity(); // 장바구니 갯수
    totalPrice += item.getQuantity() * Integer.parseInt(item.getPrice()); //가격 문자형인데 int형변환 해주기
}
model.addAttribute("size", size);
model.addAttribute("totalPrice", totalPrice);

return "cart_view";

 

[ products.html ]

상품 밑에 카트 담는 버튼 추가

<div style="position: relative">
  <p>
    <a class="btn btn-primary addToCart" th:attr="data-id=${product.id}" th:href="@{'/cart/add/' + ${product.id}}">
      장바구니담기
    </a>
  </p>
  <div class="btn btn-sm btn-success hide productAdded">추가됨!</div>
</div>

※ relative는 부모, absolute는 자식이라고 보면된다.

 

 

부모 position: relative 

자식은 css로 position: absolute 넣어서

div.productAdded 가 자식이 됨.

div.productAdded {
    position: absolute;
    bottom: 5px;
    left: 10em;
}
.hide {
    display: none;
}


= 추가! 버튼은 일단 숨기고 

나중에 장바구니 담기 버튼 누를시 '추가!' 텍스트가 뜨도록 해주자!

 

 

 <a class="btn btn-primary addToCart" th:attr="data-id=${product.id}"
      th:href="@{'/cart/add/' + ${product.id}}">
      장바구니 담기
 </a>

- 장바구니 담기 버튼을 눌렀을때 이동하는 과정

th:href="@{'/cart/add/' + ${product.id}}" 의 주소인 @GetMapping("/add/{id}") 의 return "cart_view" 페이지로 

 



 

 

- AJAX로 장바구니 추가 업데이트 시키기

위의 장바구니 담기 버튼 눌렀을때 실행하는 함수

<script>
  $('a.addToCart').click(function(e){
    alert('장바구니 누름');
  })
</script>

 

[ cart_view ] 만들기

<p>장바구니에 <span th:text="${size}"></span> 개 상품이 있습니다.</p>
<p>총 가격은 <span th:text="${totalPrice}"></span> 원 입니다.</p>
<p>
    <a href="/cart/view" class="btn btn-success">카트 보기</a>
    <a href="/cart/clear" class="btn btn-danger float-right">비우기</a>
</p>

 

결과

 

근데! 위의 cart_view 페이지로 안넘어가게 하도록 하려면

e.preventDefault();  써서 a태그의 이동요청이 취소되도록 해준다.
<script>
  $('a.addToCart').click(function(e){
    alert('장바구니 누름');
    e.preventDefault();
    let $this = $(this);
    let id = $this.attr('data-id');
    let url = '/cart/add/'+id;
  })
</script>

 

- $제이쿼리 변수명 = this 는 내가 클릭한것 
let $this = $(this);
 
 

- id가 있어야 controller 에 넘김.

let id = $this.attr('data-id'); -> 클릭한 속성값이 data-id인 product.id값을 id 변수에 저장
장바구니 담기 클릭버튼에
th:attr="data-id=${product.id}" 의 의미는 data-id의 속성이  product.id 이다

 

 

#AJAX

가져오는 주소 url / 보내는 데이터 없음 {} / 결과 데이터 data 

$.get(url,{},function(data){
  console.log(data);
});

/cart/add/{id}의 return값 cart_view.html 로 가는게 아니라 AJAX로 결과값 data를 보내줌 

왜안나오지..???

 

 

그 결과값을 아래의 장바구니 표시부분에 [cart_partial] 에 보여주기

 

AJAX

$.get(url,{},function(data){
  $('div.cart').html(data); //장바구니 덮어쓰기
}).done(function(){ //.done 성공했을때
  $this.parent().parent().find('div.productAdded').fadeIn(); //'추가!'가 희미하게 보여짐
  setTimeout(function(){ //1초동안 희미하게 사라지는 함수
    $this.parent().parent().find('div.productAdded').fadeOut(); 
  },1000);
})

 

- 장바구니 덮어쓰기

$('div.cart').html(data); 는 어디서 오느냐
<div class="cart cartActive" th:if="${cartActive}">
    <p>장바구니에 <span th:text="${csize}"></span> 개 상품이 있습니다.</p>
    <p>총 가격은 <span th:text="${ctotal}"></span> 원 입니다.</p>
    <p>
        <a href="/cart/view" class="btn btn-success">카트 보기</a>
        <a href="/cart/clear" class="btn btn-danger float-right">비우기</a>
    </p>
</div>

위의 [cart_partial.html] 의 div.cart
th:fragment="cart_partial" 

모든 페이지에 나오는 카테고리에서 cart_patial도 출력됨 
[categories.html]
<div th:replace="/fragments/cart_partial"></div>

 

 

 

- 추가! 버튼 나타났다가 사라지기 이벤트

 $this.parent().parent().find('div.productAdded').fadeIn();

장바구니 담기 버튼(this)을 눌렀을때 -> .부모(p) -> .부모(div) 에서 찾는

div.productAdded 는 추가! 버튼

fadeIn() 희미하게 보여지도록, fadeOut() 희미하게 사라지도록 함수 주기

 

 

 

☆새로고침하면 세션에서 다 비워져서 다시 원상태로 돌아간다.

반응형
LIST

장바구니는 DB저장안하고 세션으로 저장 

HttpSession session

 

세션

클라이언트 별로 서버에 저장되는 정보이다.
사용자 컴퓨터에 저장되던 쿠키와 다르게 서버에 저장되므로 비교적 보안이 필요한 데이터는 쿠키보다 세션에 저장한다.
서버가 종료되거나 유효시간이 지나면 사라진다.

 

 

세션 과정

웹 클라이언트가 서버에게 요청을 보내면 서버는 클라이언트를 식별하는 session id를 생성한다.
서버는 session id로 key와 value를 저장하는 HttpSession을 생성하고,
session id를 저장하고 있는 쿠키를 생성하여 클라이언트에게 전송한다.

클라이언트는 서버 측에 요청을 보낼 때, session id를 가지고 있는 쿠키를 전송한다.
서버는 쿠키의 session id로 HttpSession을 찾는다.

 

1. 세션 값 조회하기

getAttribute(String name) : 

 

 

2. 세션 값 저장하기

session.setAttribute(이름, 값)

 


장바구니에 추가 get매핑 add

 

- 세션에 제품 저장하기 (해쉬맵)

 

1. cart는 해쉬맵<id, 카트> 으로 장바구니를 만듬 
HashMap<Integer, Cart> cart = new HashMap<>(); 

 

2. 아이디와 카트객체들을 넣는다.
cart.put(id, new Cart(id, product.getName(), getPrice(),1,getImage()) 

 

3. 세션에 저장

session.setAttribute("cart", cart);

 

 

1. 처음 장바구니를 만들때 if

if(session.getAttribute("cart") == null) { //getAttribute : 세션값 조회 
    //1-1. 맵<id,카트> 로 리스트를 만든다.
    HashMap<Integer, Cart> cart = new HashMap<>();
    //1-2. id,카트객체들을 넣는다.
    cart.put(id, new Cart(id, product.getName(), product.getPrice(),1, product.getImage()));
    //1-3. 세션에 저장
    session.setAttribute("cart", cart);
  }

 

2. 이미 장바구니가 있을 경우  session.getAttribute("cart"); 

 - 2-1. 제품이 담긴경우

 - 2-2. 제품 없는경우

else {
    HashMap<Integer, Cart> cart = (HashMap<Integer, Cart>)session.getAttribute("cart"); 
}if(cart.containsKey(id)) {  //2-1 제품 있을경우
    int qty = cart.get(id).getQuantity(); //현재카트의 수량
    cart.put(id, new Cart(id, product.getName(), product.getPrice(),++qty, product.getImage())); //수량추가			
}else { //2-2. 제품 없는경우
    cart.put(id, new Cart(id, product.getName(), product.getPrice(),1, product.getImage()));
    session.setAttribute("cart", cart);
}

카트를 getAttribute 가져올때 object로 저장되기 때문에 해쉬맵으로 타입변환해서 가져오기

 

(경고가 뜨는데 그대로 해도 괜찮지만!) 

@SuppressWarnings("unchecked") 해준다 하지만

전체 적용을 위해 GetMapping 밑에다 넣어주기!!

else { 
    @SuppressWarnings("unchecked")
    HashMap<Integer, Cart> cart = (HashMap<Integer, Cart>)session.getAttribute("cart"); 
}

 

 

※ 헷갈릴 수 있는 것

예를들어 Cart 가 3개가 있다

(1, cart객체) 

(2, cart객체)

(3, cart객체)

 

 2-1. 제품이 담긴경우

    2번 카트의 수량을 불러와서 -> cart.get(2).getQuantity()

    수량을 업데이트 ++qty 

 2-2. 제품 없는경우

    없는 카트를 새로운 (4, cart객체) 에 만들어져서

    카트를 세션에 저장

if(cart.containsKey(id)) {  //2-1 제품 있을경우
    int qty = cart.get(id).getQuantity(); //현재카트의 수량
    cart.put(id, new Cart(id, product.getName(), product.getPrice(),++qty, product.getImage())); //수량추가			
}else { //2-2. 제품 없는경우
    cart.put(id, new Cart(id, product.getName(), product.getPrice(),1, product.getImage()));
    session.setAttribute("cart", cart);
}

 

모든 페이지에 장바구니 보여지도록

Common

 

// 현재 카트 상태 (없으면 false)
boolean cartActive = false; 

//카트가 있으면
if(session.getAttribute("cart") != null) {
    @SuppressWarnings("unchecked")
    HashMap<Integer, Cart> cart = (HashMap<Integer, Cart>)session.getAttribute("cart"); 

    int size = 0; //장바구니 상품 갯수
    int totalPrice = 0; //총 가격

    //장바구니 cart객체 반복 (1,cart객체) (2,cart객체)... id빼고 객체만
    for(Cart item : cart.values()) {
        size += item.getQuantity(); // 장바구니 갯수
        totalPrice += item.getQuantity() * Integer.parseInt(item.getPrice()); //가격 문자형인데 int형변환 해주기
    }
    model.addAttribute("size", size);
    model.addAttribute("totalPrice", totalPrice);

    cartActive = true;
}

model.addAttribute("cartActive", cartActive); //없으면 false 있으면 true

??

model.addAttribute("cartActive", cartActive); 

는 어떻게 보여질지 모르겠음..

 

 

☆주의

totalPrice += item.getQuantity() * Integer.parseInt(item.getPrice()); 

cart의 price는 String이고 totalPrice은 int 

형변환해주기!!

반응형
LIST

Common

페이지마다 카테고리 공통적으로 들어가도록 ccategories가 모든 카테고리

List<Category> categories = categoryRepo.findAll();

model.addAttribute("ccategories", categories);

 

 

fragments폴더안에 categories.html 생성

<div class="col-3" th:fragment="categories">
    <h3 class="display-4">Categories</h3>
    <ul class="list-group">
        <li class="list-group-item">
            <a href="/category/all" class="nav-link">All Products</a>
        </li>
        <li class="list-group-item" th:each="category: ${ccategories}">
            <a class="nav-link" th:href="@{'/category/' + ${category.slug}}" th:text="${category.name}"></a>
        </li>
    </ul>
</div>
th:fragment="categories"
th:each="category: ${ccategories}"

모든 카테고리를 받고 

 

 

모든 페이지에 적용하도록 page.html에 fragment를 받는다.

<div th:replace="/fragments/categories :: categories"></div>

 

 

- 카테고리 클릭시 링크주소를 만든다

CategoriesController 만들기
@GetMapping("/{slug}")
public String category(@PathVariable String slug, Model model,@RequestParam(value = "page",defaultValue = "0") int page) {

    int perPage = 4; //한페이지에 4개
    Pageable pageable = PageRequest.of(page, perPage); //표시할페이지, 한페이지당 몇개(4개)
    long count = 0;

    //카테고리 선택 (전체,과일,채소)
    if(slug.equals("전체")) {
        Page<Product> products = productRepo.findAll(pageable);
        count = productRepo.count(); //전체 제품 수

        model.addAttribute("products", products);			
    }else { //카테고리별 페이징
        Category category = categoryRepo.findBySlug(slug);
        if(category == null) { //카테고리 없으면
            return "redirect:/"; //홈페이지로
        }
        int categoryId = category.getId();
        String categoryName = category.getName();
        //해당 페에지에 제품 수 (페이지네이션)
        List<Product> products = productRepo.findAllByCategoryId(categoryId,pageable);
        count = productRepo.countByCategoryId(categoryId);

        model.addAttribute("products", products); //선택한 카테고리의 제품들
        model.addAttribute("categoryName", categoryName);
    }

    double pageCount = Math.ceil((double)count / (double)perPage);
    model.addAttribute("pageCount", (int)pageCount); //총페이지
    model.addAttribute("perPage", perPage); 		 //한 페이지당 상품갯수
    model.addAttribute("count", count);				 //전체 상품개수
    model.addAttribute("page", page); 				 //현재 페이지


    return "products";
}

 

- 총페이지(pageCount) 계산은  타입을 double로 하여 소수점 나오도록  

13/6개 = 2.1(3페이지)

 

 

 

페이지별 카테고리

ex) 페이지네이션과 해당 제품들 (2페이지에 카테고리 4개로 설정해놓은)

List<Product> products = productRepo.findAllByCategoryId(categoryId,pageable);
count = productRepo.countByCategoryId(categoryId);

long count = productRepo.count();  //전체 상품갯수

페이지를 보여주기 위한 계산이 위에 메서드로 정의함

 

 

 

 

 

카테고리(전체,과일,채소)별로 클릭 할때 나오는 상품view 만들기

 

[ products.html ]

th:text="${categoryName} ?: '모든제품'" 
→ 참은 생략되어있고 없으면 모든제품

※  ?:  (띄어쓰지말고 붙여써야 오류안남)

 


 

오류

Parameter value[1] did not match expected type ~

 


해결

 

Category - 카테고리 id는 int 


Product - 카테고리 id는 String


[ CategoriesController ]

String categoryId = Integer.toString(category.getId());

 

카테고리id int=>String 변환

 

반응형
LIST

[ PageController ]

@Controller
@RequestMapping("/")
public class PageController {
	
	@Autowired
	private PageRepository pageRepo;
	
	@GetMapping
	public String home(Model model) {
		
		Page page = pageRepo.findBySlug("home");
		model.addAttribute("page", page);
		
		return "page";
	}
}

관리자에서 만든 PageRepository에서 슬러그 home을 가져와 page뷰로 넘겨준다 (=메인홈페이지)

 

 

 

 

 

[ page.html ]

<!-- 고객용 -->
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
  <head th:replace="/fragments/head :: home-customer"> </head>
  <body>
    <nav th:replace="/fragments/nav :: nav-customer"></nav>

    <main class="container-fluid mt-5"> <!-- fluid는 가로 해상도에 상관없이 100%의 width-->
      <div class="row"> <!--한 줄-->
        
        <div class="col"></div> <!-- 1/12 -->
        <div class="col-7" th:utext="${page.content}"></div> <!-- 7/12 --><!--utext는 ck에디터로 입력해서 DB에도 어떤태그쓴지 알기위해-->
        <div class="col"></div> <!-- 1/12 -->
      </div>
    </main>

    <footer th:replace="/fragments/footer :: footer"></footer>
  </body>
</html>

 

th:utext="${page.content}" 

페이지 home의 content수정할때 DB에 태그까지 저장되게 하는 th:utext

 

DB에 저장

 

DB에 태그까지 저장 되도록 쓰는 이유는??

ck로 태그가 포함된 문서편집을 웹에서 하니까 DB에 태그를 표시해서 자세히 알려고 

 

 

 

page 수정 눌러서 home 페이지 내용을 직접 넣고

http://localhost:8080/ 

- nav.html 에서 페이지를 반복문으로 표시하도록 하자

네브바의 페이지들은 모든 컨트롤러에 공통적으로 적용되어야해서

Common 클래스를 따로 만들어서 페이지 리스트 순서대로 nav바에 전달 되도록 cpages를 만든다.

 

Common
//모든 컨트롤러에 적용(모든 페이지에 적용)
@ControllerAdvice
public class Common {

	@Autowired
	private PageRepository pageRepo;
	
	//모델에 추가한다.
	@ModelAttribute
	public void sharedData(Model model) {
		//cpages에 모든 페이지를 순서대로 담아서 전달
		List<Page> cpages = pageRepo.findAllByOrderBySortingAsc();
		model.addAttribute("cpages", cpages);
	}
}

@ControllerAdvice : 공통으로 모든 페이지에 적용해야함 

 

 

페이지 순서대로 담은 리스트들(cpages) -> th:each=" page : ${cpages}" 로 전달

 

[ nav.html ]

<div class="collapse navbar-collapse" id="navbarNav">
    <ul class="navbar-nav">
      <li class="nav-item active" th:each="page : ${cpages}">
        <a class="nav-link" th:href="@{'/' + ${page.slug}}" th:text="${page.title}"></a>
      </li>
    </ul>
</div>

 

결과

페이지가 순서대로 정렬이 됨.

 

Home은 표시안되도록 a태그에 if절 추가하기 (쇼핑몰을 누르면 Home 페이지로 넘어가기 때문에)

 th:if="${page.slug != 'home'}" th:href="@{'/'+ ${page.slug}}"

 

 

 

- 페이지 주소는 슬러그로 해놨으니까 해당 슬러그의 페이지로 이동하도록 get매핑 만든다.

//슬러그가 들어올경우 슬러그가 해당하는 페이지를 출력한다.
@GetMapping("/{slug}")
public String home(@PathVariable String slug, Model model) {
    //슬러그를 가져와서 메인홈페이지를 만들어준다.
    Page page = pageRepo.findBySlug(slug);

    if(page == null) {
        return "redirect:/"; //페이지가 없으면 홈페이지로 이동
    }

    model.addAttribute("page", page);

    return "page";
}
반응형
LIST

+ Recent posts