안녕하세요, Brad입니다. 오늘도 어제 이어서 '회원가입, 사용자 목록 기능 구현'을 계속 진행했는데요. 미션을 진행하면서 어제보다 많은 부분에서 지식의 빈틈이 매꾸어질 수 있는 유익한 시간이었습니다. 그럼 배운 것들 정리해볼께요!
만약 index.html을 templates폴더 밑으로 옮기면 어떻게 주소를 입력해야 index.html로 접근할 수 있을까요? templates폴더 바로 밑에 index.html을 두었을 때는 'localhost:8080'까지만 입력하면 제대로 접근되는 것을 알 수 있었습니다. 그 이유는 template engine에서 알아서 templates폴더 바로 밑에 index.html을 찾아서 연결해주기 때문입니다. 그래서 templates > common 이라는 폴더 안에 있을 때는 제대로 찾지 못하구요. 만약 그렇게 하고 싶으면 따로 설정을 해줘야하는데요. 그 부분은 좀있다가 살펴볼게요.
HTML 줄이기
웹 사이트를 보면 각 페이지마다 navBar나 css를 불러오는 head부분에서 흔히 중복되는 코드를 사용하는데요(body부분만 바뀌기 때문). 이런 경우 중복된 코드를 한 곳에 모아두고 그 코드가 필요한 부분에서 template engine 중 handlebar를 사용하면 쉽게 불러올 수 있습니다. 주의할 점은 template engine 을 쓰는 것이기 때문에 공통적으로 사용하는(중복된 코드를 가진) html파일이 templates 폴더 내에 위치해야 제대로 적용 가능합니다. 같은 경로 위치면 {{>html이름}}
로 사용가능하나 경로를 바꾸고 싶다면 /
(슬러쉬)를 써서 경로 설정 가능하구요!
HTML을 몰아서 사용하니 가장 좋은 점은 변화가 생겼을 때 한 부분만 바꿔주면 모두 적용된다는 점입니다. 이전에 다른 탭으로 이동할 때 상대경로로 설정했었는데요. 그렇다보니 html파일 경로 변경이 발생하면 그 부분과 연결된 모든 곳에서 연결 오류 문제가 발생하였고 따로 다 설정했어야 했거든요.
하지만 이렇게 모으고 나니 공통된 html을 가진 다른 html파일들에서, 즉 {{>html이름}}
을 사용하는 모든 html파일에서 a태그나 다른 연결이 제대로 작동하기 위해 '상대경로'가 아닌 '절대경로'가 필요해졌습니다. 그런데 그 탭과 연결될 html파일들을 template 내로 이동했기 때문에 주소창에서 바로 접근이 안되고 Mapping이 요구되었습니다. 그래서 결국 어제한 것과 같이 각각의 @GetMapping("/users/form")
이나 @GetMapping("/users/login")
메서드들을 만들어야 하는데 미션 힌트를 통해서 컨트롤러에서 굳이 메서드 만들 필요없이 해결할 수 있는 방법을 배웠습니다! 자바 base 패키지 내에 config 패키지를 만들고 MvcConfig
클래스를 만들어 다음과 같이 구현할 수 있습니다.
@Configuration
public class MvcConfig extends WebMvcConfigurerAdapter {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.setOrder(Ordered.HIGHEST_PRECEDENCE);
registry.addViewController("/users/form").setViewName("user/form");
registry.addViewController("/users/login").setViewName("user/login");
registry.addViewController("/questions/form").setViewName("qna/form");
}
}
위와 같이 구현하여 제일 윗 부분의 경우 특정 태그에서 '/users/form'의 연결 요청을 하면 'user/form'으로 바로 연결해줄 수 있었습니다.
아까 위에서 index.html을 templates폴더 밑으로 옮기고 나서 'localhost:8080/index.html'로는 접근이 안되고 'localhost:8080'로만 접근할 수 있다고 말씀드렸는데요. 위 코드를 이용하면 registry.addViewController("/index.html").setViewName("/");
로 설정하여 비록 index.html은 templates 안에 있지만 Mapping을 통해 index.html로 연결될 수 있습니다.(setViewName("/index")
아님!). 그리고 만약 메서드로 정의한다면 다음과 같이 정의할 수 있을 것입니다.
처음에 경로를 적을 때는 {{>common/head}}
로 하지 못했는데요. 왜냐하면 만약 user/form.html이라면 user위에 common이 있어 {{>../common/head}}
로 하였기 때문입니다. 그런데 다음과 같은 오류 메시지를 보고 해당 html파일의 경로가 아니라 templates폴더가 default로 작동하는 것을 알게되었습니다.
절대경로와 상대경로
이번 미션을 진행하면서 .
(점)이 있고 /
(슬러시)가 있고 없고에 따라 절대경로, 상대경로가 나뉘고 그에 따라 제대로 경로가 설정이 되는지 여부를 경험해볼 수 있었습니다. 그리고 이러한 경험을 통해서 제일 앞에 .
나 ..
를 통해 경로를 설정해주면 상대경로 /
로 시작되면 절대경로로 설정됨을 알 수 있었습니다. 먼저 상대경로부터 보시죠.
./
로 시작하여 상대경로로 설정했음을 알 수 있습니다. 이렇게 설정하면 실제 a태그를 연결했을 때 URL은 다음과 같이 되는 것이죠.
원래 users
에 있었는데 덧붙여져 users/users/login
으로 연결이 됐네요. 그렇다보니 연결시키고 싶었던 페이지가 뜨지 않았던 것입니다. 이제는 절대경로를 보시죠.
/
로 시작하여 딱 /
부터 시작하여 절대적인 경로로 사용함을 명시하였습니다.
이렇게 설정하니 기존 /users
에서 덧붙이지 않고 루트(/
)부터 다시 /users/login
으로 제대로 연결되었음을 볼 수 있습니다!
html중복을 피하기 위해 head 부분을 따로 빼냈는데요. 여기 안에 있던 css경로도 '절대경로', '상대경로' 때문에 혼동이 있었습니다. 처음에 아래와 같이 상대경로로 했었거든요.
css가 /
바로 밑에 있다는 것을 고려하면 저렇게 상대경로로 해선 안되었습니다. 그리고 공유하는 그 html파일을 불러오는 시점의 경로가 다 다르기 때문에 어떤 것은 제대로 css를 불러올 수도 있지만 어떤 것은 제대로 css를 못 불러올 수 있는 것이죠.
따라서 다음과 같이 절대경로를 이용하여 제대로 설정할 수 있었습니다.
사용자 정의 객체에서 정의된 속성이지만 form태그 내에 그 값이 없었다면?
위와 같은 경우인데요. 물어보기 위한 글을 작성하는 페이지에서 '글쓴이', '제목', '내용'을 받기 위한 Question
클래스 Java Bean규약에 따라 만들어놓았습니다. 그런데 작성시간을 알기위해 Question
객체 내에 time이라는 속성을 정의해둔 것이죠. 그런데 form태그 내에서는 없구요. time은 자바 코드 내에서 처리할 것이거든요.
어제 Java Framework에 정리할 때 위와 같은 경우 알아서 Question
객체를 넘긴다고 했는데요. 그 객체를 출력해보면 time 속성은 'null'값으로 들어온 것을 확인할 수 있었습니다.
mustache 에서 값 하나 넘길 때도 반복문처럼 해야하나?
mustache를 이용하여 model에 List값을 넣어 넘길 때 {{#users}}
{{/users}}
로 묶어놓고 그 안에 {{속성이름}}
을 사용하면 각 Element마다 반복된 html 안에서 각각의 값이 넣어져 쉽게 사용할 수 있었는데요. 그럼 값을 하나를 넘긴다면 {{#users}}
{{/users}}
처럼 묶어줘야할까요? 그러니까 이번엔 List값이 하니라 User
의 객체 user 단 하나인거죠. 테스트를 통해 알아보니 {{#user}}
{{/user}}
처럼 똑같이 묶어주어야 그 안의 속성값이 제대로 매칭됨을 알 수 있었습니다.
경로 길이는 짧을수록 좋다
같은 경로 ("/users/{userId}")이더라도 GET방식이냐, POST방식이냐에 따라 구별되어 쓸 수 있습니다. 아무생각 없이 세 번쨰의 경우 "users/{userId}/update"로 경로설정을 해두었는데 피드백으로 경로가 짧을수록 좋고 명확하다는 얘기를 들었습니다. 그래서 의미상 혼동되지 않고 또 중복되지 않는 방식(GET방식, POST방식 이외 다른 2가지 방식이 더 있다고 들었습니다)이면 짧게 경로를 지정하는게 좋습니다.
하지만 위의 경우와 같이 경로로 변수를 받을 때는 {index}를 꼭 받기 위해 더 이상 줄일 수 없겠죠!
stream 을 사용하여 어떻게 특정 element만 바꿀 수 있을까?
처음에 위와 같이 가져왔습니다. 근데 filter()
를 사용하니까 조건을 만족하는 user을 가져와 제가 원하는 users를 모두 얻을 수 없었습니다.
따라서 map만 사용해야 하는데요. map 안에서 위와 같이 조건문 사용하여 저희가 찾는 값이면 저희가 원하는 값으로 user를 바꾸고, 조건을 만족하지 않으면 기존값 계속 사용하게 하는 것입니다.
참! 위에서 users에서 stream을 사용할 때 처음엔 user = user.stream
으로 시작하지 않고 users.stream
으로 시작하였습니다. 알아서 stream을 사용하면 얕은 복사로 users와 연결된 것인줄 알았거든요. 근데 값을 가공한뒤 출력해서 보니 users 는 변한게 없더라구요. 그래서 stream의 가공된 값이 users 값에 영향을 주지 않는 깊은 복사가 이루어지고 있음을 알 수 있었습니다. 그래서 users =
로 받아 덮어쓰도록 하였습니다.
오늘은 여기까지 하고 PR을 보냈구요. 내일은 피드백 받은 것들을 바탕으로 다시 좀 더 정리해봐야겠습니다!!
오늘 추가적으로 공부한 부분
'TIL' 카테고리의 다른 글
Today's Dev Notes(2018-11-15) (0) | 2018.11.15 |
---|---|
Today's Dev Notes(2018-11-14) (1) | 2018.11.14 |
Today's Dev Notes(2018-11-12) (0) | 2018.11.13 |
Today's Dev Notes(2018-11-04) (0) | 2018.11.04 |
Today's Dev Notes(2018-11-01) (0) | 2018.11.01 |