
먼저 문제 원본은 아래와 같다.
문제에서 실제로 구현해야 하는 조건만 다시 정리하면 이렇다.
- 요청 주소는
/study/{id}/thymeleaf형식이다.{id}위치에 들어오는 숫자에 따라 서로 다른 외부 사이트로 이동해야 한다.- 컨트롤러 메서드는 한 개만 만들어서 해결해야 한다.
- 요청 주소 안의
{id}값은@PathVariable로 받는다./study/1/thymeleaf요청은https://abbo.tistory.com/56으로 이동한다./study/2/thymeleaf요청은https://abbo.tistory.com/57으로 이동한다./study/3/thymeleaf요청은https://www.thymeleaf.org/doc/tutorials/3.1/usingthymeleaf.html로 이동한다./study/4/thymeleaf요청은https://www.baeldung.com/dates-in-thymeleaf로 이동한다.이 문제의 핵심은 화면에 값을 출력하는 것이 아니라, 요청 주소에 포함된 숫자를 읽고 그 숫자에 맞는 외부 사이트로 바로 이동시키는 것이다.
이 문제는
Thymeleaf화면을 만들어서 링크를 보여주는 문제가 아니다.
요청 경로에 들어온 숫자를 읽고, 그 숫자에 따라 컨트롤러에서 바로 다른 사이트로 이동시키는 문제다.
처음에는Model에studyUrl값을 담아서 처리하려고 했다.
하지만Model은 값을 화면으로 넘겨줄 때 사용하는 객체다.
즉,model.addAttribute("studyUrl", "...")로 값을 담으면 그 값을 보여줄Thymeleaf파일이 필요하다.
이번 문제는 별도의 화면 구현 없이 바로 이동해야 하므로Model을 사용할 필요가 없다.
대신 컨트롤러 메서드에서 문자열을 반환할 때 앞에redirect:를 붙이면 된다.
이 흐름을 정리하면 아래와 같다.
요청 주소 입력 → @PathVariable로 id 추출 → id 값에 따라 redirect 주소 선택 → 브라우저가 외부 사이트로 이동
- 요청 주소
/study/{id}/thymeleaf구조 확인{id}값을 받기 위해@PathVariable사용- 처음 작성한
Model방식의 문제점 확인redirect:방식으로 외부 사이트 이동 처리id값에 따라 다른 주소를 반환하도록if문작성- 잘못된 번호가 들어왔을 때 기본 이동 경로 처리
- 최종 코드 작성
- 요청 주소별 이동 결과 확인
이번 실습은 컨트롤러 메서드 하나로 여러 요청을 처리해야 한다.
따라서 요청 주소의 일부를 변수처럼 받아오는@PathVariable이 핵심이다.
여기서 중요한 점은Model에 값을 담는 방식과redirect:로 이동시키는 방식이 완전히 다르다는 것이다.
Model은 화면에 데이터를 넘길 때 쓰고,redirect:는 브라우저를 다른 주소로 이동시킬 때 쓴다.
1. 요청 주소 구조 확인
문제에서 요구한 요청 주소는 아래처럼 생겼다.
/study/1/thymeleaf /study/2/thymeleaf /study/3/thymeleaf /study/4/thymeleaf여기서 바뀌는 부분은 가운데 숫자다.
즉,/study/와/thymeleaf는 고정이고, 가운데 숫자만 요청마다 달라진다.
이럴 때는 요청 매핑 주소를 아래처럼 작성할 수 있다.@RequestMapping("/study/{id}/thymeleaf")
{id}는 실제 요청 주소에 들어오는 값을 변수처럼 받겠다는 의미다.
예를 들어/study/3/thymeleaf로 요청하면{id}자리에는3이 들어온다.
데이터 흐름
// 사용자가 입력한 요청 주소 /study/3/thymeleaf // 컨트롤러 매핑 주소 /study/{id}/thymeleaf // 실제로 추출되는 값 id = 3→ 사용자가
/study/3/thymeleaf로 요청한다.
→Spring MVC는/study/{id}/thymeleaf패턴과 요청 주소를 비교한다.
→ 가운데 숫자3을{id}값으로 인식한다.
→ 그 값이 컨트롤러 메서드의id매개변수로 들어간다.
즉, 이 문제에서는 요청 주소에 들어온 숫자를 먼저 꺼내야 하고, 그 숫자에 따라 이동할 주소를 정해야 한다.
2. 처음 작성한 Model 방식의 문제점
처음에는 아래처럼
Model에 이동할 주소를 담았다.package com.example.springlab.controller; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; @Controller public class StudyPathController { @RequestMapping("/study/{id}/thymeleaf") public String study(@PathVariable int id, Model model) { if (id == 1) { model.addAttribute("studyUrl", "https://abbo.tistory.com/56"); } else if (id == 2) { model.addAttribute("studyUrl", "https://abbo.tistory.com/57"); } else if (id == 3) { model.addAttribute("studyUrl", "https://www.thymeleaf.org/doc/tutorials/3.1/usingthymeleaf.html"); } else if (id == 4) { model.addAttribute("studyUrl", "https://www.baeldung.com/dates-in-thymeleaf"); } return " "; } }이 방식의 문제는
Model에 값을 담았다고 해서 브라우저가 자동으로 이동하지 않는다는 점이다.
Model은 컨트롤러에서 만든 데이터를 화면 파일로 전달할 때 사용하는 객체다.
즉, 위 코드에서studyUrl에 주소를 담으면 그 주소를 사용할Thymeleaf파일이 필요하다.
예를 들어 화면에서 아래처럼 값을 꺼내서 링크로 보여주거나 이동 처리를 해야 한다.<a th:href="${studyUrl}">이동하기</a>하지만 문제에서는
Thymeleaf화면을 구현하라고 하지 않았다.
요청 패스에 따라 바로 다른 사이트로 이동하라고 했다.
그래서 이 문제는Model에 주소를 담는 방식보다 컨트롤러에서 바로redirect:를 반환하는 방식이 맞다.
데이터 흐름
// 컨트롤러에서 Model에 값 저장 model.addAttribute("studyUrl", "https://abbo.tistory.com/56"); // 이 값은 화면 파일에서 꺼내 써야 한다. studyUrl = "https://abbo.tistory.com/56" // 하지만 브라우저가 자동으로 이동하는 것은 아니다.→
model.addAttribute()는 데이터를 저장하는 코드다.
→ 저장한 데이터는Thymeleaf같은 화면 파일에서 꺼내 써야 한다.
→ 컨트롤러가 주소를Model에 담았다고 해서 브라우저가 자동으로 외부 사이트로 이동하지 않는다.
→ 따라서 화면 파일 없이 바로 이동하려면redirect:를 사용해야 한다.
정리하면Model은 값을 화면에 전달하는 용도이고,redirect:는 브라우저를 다른 주소로 이동시키는 용도다.
3. redirect 방식으로 외부 사이트 이동 처리
화면 파일 없이 바로 이동하려면 반환 문자열 앞에
redirect:를 붙이면 된다.return "redirect:https://abbo.tistory.com/56";이 코드는
abbo.tistory.com/56이라는 문자열을 화면 이름으로 찾는 것이 아니다.
Spring MVC에게 이 주소로 다시 이동하라는 응답을 만들라고 알려주는 코드다.
컨트롤러에서redirect:를 반환하면 서버는 브라우저에게 이동할 주소를 알려준다.
그 다음 실제 이동은 브라우저가 다시 요청하면서 이루어진다.
데이터 흐름
// 1. 사용자가 요청 /study/1/thymeleaf // 2. 컨트롤러에서 반환 return "redirect:https://abbo.tistory.com/56"; // 3. 브라우저가 최종 이동 https://abbo.tistory.com/56→ 사용자가
/study/1/thymeleaf로 요청한다.
→ 컨트롤러는id값이1인지 확인한다.
→id가1이면"redirect:https://abbo.tistory.com/56"을 반환한다.
→Spring MVC는 브라우저에게 해당 주소로 이동하라는 응답을 보낸다.
→ 브라우저는 외부 사이트로 다시 요청을 보내고 화면이 이동된다.
이 흐름을 알면return "thymeleaf"와return "redirect:..."의 차이도 이해하기 쉽다.
return "thymeleaf"는 화면 파일 이름을 찾는 것이고,return "redirect:..."는 브라우저를 다른 주소로 이동시키는 것이다.
4. id 값에 따라 이동 주소 나누기
이제
@PathVariable로 받은id값에 따라 이동할 주소를 나누면 된다.if (id == 1) { return "redirect:https://abbo.tistory.com/56"; } else if (id == 2) { return "redirect:https://abbo.tistory.com/57"; } else if (id == 3) { return "redirect:https://www.thymeleaf.org/doc/tutorials/3.1/usingthymeleaf.html"; } else if (id == 4) { return "redirect:https://www.baeldung.com/dates-in-thymeleaf"; }여기서는 문자열 비교가 아니라 숫자 비교다.
그래서equals()가 아니라==를 사용해도 된다.
id는int타입이다.
int는 객체가 아니라 기본 자료형이기 때문에 값 자체를 비교할 때==를 사용한다.
데이터 흐름
// 요청 주소 /study/2/thymeleaf // @PathVariable 결과 id = 2 // 조건문 흐름 if (id == 1) { // 실행 안 됨 } else if (id == 2) { return "redirect:https://abbo.tistory.com/57"; } // 최종 이동 https://abbo.tistory.com/57→ 사용자가
/study/2/thymeleaf로 요청한다.
→{id}자리에 있는2가int id로 들어온다.
→ 첫 번째 조건id == 1은false다.
→ 두 번째 조건id == 2가true다.
→ 컨트롤러는"redirect:https://abbo.tistory.com/57"을 반환한다.
→ 브라우저는 해당 주소로 이동한다.
즉,if문은 요청 주소에서 꺼낸 숫자를 보고 어떤 사이트로 보낼지 선택하는 역할을 한다.
5. 잘못된 번호가 들어온 경우 처리
문제에는
1부터4까지의 요청만 제시되어 있다.
하지만 사용자가/study/10/thymeleaf처럼 없는 번호로 요청할 수도 있다.
이런 경우 아무 값도 반환하지 않으면 문제가 생긴다.
그래서 마지막에는 기본 이동 경로를 하나 작성해두는 것이 안전하다.return "redirect:/";이 코드는 조건에 맞는 번호가 없으면 프로젝트의 기본 경로
/로 이동시키겠다는 의미다.
데이터 흐름
// 요청 주소 /study/10/thymeleaf // @PathVariable 결과 id = 10 // 조건문 확인 id == 1 // false id == 2 // false id == 3 // false id == 4 // false // 기본 이동 return "redirect:/";→ 사용자가 문제에서 정한 범위 밖의 숫자를 요청한다.
→ 모든if,else if조건이false가 된다.
→ 마지막return "redirect:/";가 실행된다.
→ 브라우저는 기본 경로/로 이동한다.
이렇게 마지막 반환값을 작성하면 예상하지 못한 요청이 들어와도 컨트롤러 메서드가 정상적으로 끝난다.
6. 최종 동작 흐름
전체 흐름은 아래처럼 정리할 수 있다.
/study/1/thymeleaf → id = 1 → redirect:https://abbo.tistory.com/56 /study/2/thymeleaf → id = 2 → redirect:https://abbo.tistory.com/57 /study/3/thymeleaf → id = 3 → redirect:https://www.thymeleaf.org/doc/tutorials/3.1/usingthymeleaf.html /study/4/thymeleaf → id = 4 → redirect:https://www.baeldung.com/dates-in-thymeleaf요청 주소는 하나의 패턴으로 받지만, 실제 이동 주소는
id값에 따라 달라진다.
이 문제의 핵심은 컨트롤러 메서드를 여러 개 만드는 것이 아니라, 하나의 메서드에서@PathVariable값을 기준으로 분기 처리하는 것이다.
실제로 제출할 수 있는 최종 코드는 아래와 같다.
Model은 사용하지 않고,redirect:반환값만 사용한다.package com.example.springlab.controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; @Controller public class StudyPathController { @RequestMapping("/study/{id}/thymeleaf") public String study(@PathVariable int id) { if (id == 1) { return "redirect:https://abbo.tistory.com/56"; } else if (id == 2) { return "redirect:https://abbo.tistory.com/57"; } else if (id == 3) { return "redirect:https://www.thymeleaf.org/doc/tutorials/3.1/usingthymeleaf.html"; } else if (id == 4) { return "redirect:https://www.baeldung.com/dates-in-thymeleaf"; } return "redirect:/"; } }
@PathVariable은 요청 주소 안에 들어온 값을 매개변수로 받을 때 사용한다./study/{id}/thymeleaf에서{id}는 실제 요청 주소의 숫자 부분을 받는 자리다.Model은 컨트롤러 데이터를 화면 파일로 넘길 때 사용한다.Model에 주소를 담는다고 해서 브라우저가 자동으로 이동하지 않는다.- 화면 파일 없이 바로 이동하려면
return "redirect:주소"형식으로 작성한다.return "redirect:https://..."는 외부 사이트로 이동시킬 때 사용할 수 있다.int타입 값 비교는equals()가 아니라==로 처리한다.- 컨트롤러 메서드를 하나만 만들어도
@PathVariable과 조건문을 사용하면 여러 요청을 처리할 수 있다.- 문제에서 요구한 주소를 그대로 확인해야 한다. 특히
/study/2/thymeleaf는56이 아니라57로 이동해야 한다.