설정 잘못했다가 경로 인식 못해서 회사에서 너무 민망했는데 (아무리 초보들꺼여도 우리플젝 욕먹음 당연 속상함) 이게 설정을 블로그 짜깁기로 보다보니 제대로 꼬여서 그런거였다. 스프링부트 버전이 높아서 인식이 안되나 별별 생각을 다 했는데 집에 와서 찬찬히 풀어보니까 되네 돼!!!!!!

 

 


 

 

1. 일단 jsp를 설정하며 마주친 에러.

jstl에 대한 dependency를 꽂았는데 나는 분명 처음에 에러가 안났다. 그래서 여기에 문제가 있는줄 몰랐는데 pom파일부터 차근차근 다시 저장하면서 갑자기 에러가 생겼다..? 문제가 있음 빨리빨리 말해라...!!!!!

 

Multiple markers at this line - Project build error: 'dependencies.dependency.version' for javax.servlet:jstl:jar is missing. - 'dependencies.dependency.version' for javax.servlet:jstl:jar is missing.

 

starter 는 중복이라 주석처리함

 

👉 에러창을 읽어보면 jstl:jar missing 그리고 version이 missing이라고 나온다.

각종 블로그글에는 embed-jasper와 jstl 세팅만 있고 jstl의 버전설정이 빠져있다. gpt도 마찬가지. 그냥 버전을 추가해주면 된다...하.... 🤦‍♀️🤦‍♀️🤦‍♀️

 

 


 

 

2. 타임리프가 경로를 못찾아요

application 세팅에서 jsp와 타임리프는 각각 따로 설정을 해주어야 한다. 그래서 yml서식에 맞게 잘! 들여쓰기로! 해줬는데! 자꾸 아래처럼 타임리프를 못찾는다고 나오는것....환장..... templates 폴더 하위에 thymeleaf폴더 잘 만들어줬는데 왜 못찾니 한참을 고민함.

 

Error resolving template [thymeleaf/index], template might not exist or might not be accessible by any of the configured Template Resolvers

 

회사에서 이거만 한시간을 검색하고 고민했을것임. 폴더를 통채로 붙여넣었어도 노트북이 새로 바뀌면서 설정이 뭔가 바꼈을수 있다...는 말씀을 듣고 그런가...싶어서 이거 할 마음이 뚝 사라졌었음. 

 

그리고 또다시 여러 글을 읽어보는데, 다른분의 설정파일에서 나랑 다른걸 발견했다. 하..설마..? 하고 추가했는데 맞았음.  

 

 

👉 나는 prefix에 /templates/만 써놓았어서 분명 해당폴더에 다 들어가있는데 한참 찾았더니 classpath:가 추가되어야 했다. 

 


 

그래서 결국 인덱스 화면 잘 뜹니다 ^^...

 

함정은 나는 관리자화면에서 연습하려고 했더니 거기까지 로그인하고 뭐하고 하는 여정이 너무 길다. 왜냐. 이제 디폴트가 jsp로 변경되었기때문에 (return "index"는 jsp를 찾아감) 기존의 타임리프 화면은 retun에다가 모두 타임리프를 붙여줘야 하기 때문이죠... (뷰 네임에 그렇게 세팅되어있으므로) 

 

이렇게 말입니다

 

 

이 많은 화면에 다 붙이는건 쓸데없는 노가다이므로 그냥 로그인없이 접근하는 화면 하나 만들어서 작업해야겠다.

으휴 ㅠㅠ 개똥멍청이 될뻔했는데 그래도 멍청이정도는 되겠지 이제. 못살아..............

 

 

 

 

인간은 원래 해본것만 하려하고 (쉬우니까) 안해본걸 어려워하지..... ㅠㅠ  이걸 이렇게 헤맬줄이야.

 

회사 노트북에 개인 프로젝트를 다시 구동시키게 됐는데 (DB는 도커 & DBeaver로!)

자바 깔고 STS 설치하고 maven이랑 톰캣깔고 어... path는 다 잘 잡혔는데 왜 에러밭이지 ㅠㅠ...하면서 보니까 Builder..는 롬복 기능인데 이런것도 다 일일히 .jar 파일을 받았었구나 하면서 기억을 더듬어가는 중.

boot가 다해줘요~로 마지막 기억이 남아서인지 하나하나 빌드업했던게 기억들이 안난다.

 

lombok 설치하고 maven 업데이트 하니까 에러창 싸아악 사라지는거보고 숨멎할뻔했던 정신이 돌아왔다.

생각해보면 환경구축도 1~2번밖에 안해본 셈이니 허둥지둥 할만하다고 스스로를 다독여본다.......😅

 


 

 

그리고 이제 도커에 올려둔 mariaDB와의 연결문제가 생겼음. 에러가 뜸 ^^!!!!

혹시나해서 원래 쓰던 팀서버와도 연결해봤는데 아주 잘~ 작동하는걸 봐서는 이건 DBeaver와의 연결문제임 코드문제가 아님 ^^....

 

 

Access denied for user 'root'@'172.17.0.1' (using password: YES)

 

[오류 해결] [Docker][MySQL] Access denied for user 'root'@'172.17.0.1' (using password: YES)

Docker에 Mysql을 설치하고 웹프로젝트 실행하였는데 접속 오류가 발생하였습니다. Cannot create PoolableConnectionFactory (Access denied for user 'root'@'172.17.0.1' (using password: YES) 그래서 해결을 하려고 찾아보니

csksoft.tistory.com

 

[Docker][MySQL]Access denied for user ‘’@’172.17.0.1'(using password: YES)

Problem: Using $ mysql or mysql-workbench or navicat15 or SQLPro for Mysql to connect to the docker-mysql of this machine, the…

medium.com

 

본격적으로 오류를 수정해보려던 찰나... 분명 url에서 localhost로만 바꿔주면 된다는 말을 들었는데 ... 라고 생각해서 password를 봤더니 mariadb만 다르게 설정되어있었다 ^^... 패스워드 바꿔주니까 잘 접속됐음. (이와중에 localhost:3306/DB이름? <-여기 매칭 안시켜줘서 자꾸 못찾는다고 함. 대소문자도 구별하더라...)

 

boot의 application-dev.yml파일에서 서버 접속을 설정할땐 
 url: jdbc:mariadb://localhost:3306/octapdb?serverTimezone=Asia/Seoul&characterEncoding=UTF-8
이렇게 localhost로 두고, 도커에서 3306:3306 으로 컨테이너를 생성했으므로 내가 3306으로 접속 ▶ 해당 컨테이너 ▶ octapdb로 이동이 다이렉트로 이루어진다. 너무 신기했음. 당연히 컨테이너는 running 이어야 한다!

 

 


 

 

하지만 localhost:8081로 접속하면 아래와 같이 에러메세지가 뜨면서 프로젝트에 세팅해놨던 '접속불가페이지라 홈화면으로 이동합니다' 화면이 무한루프로 돌았다... 이때부터 권한문제라는 생각이 들었다. (내가 만들었던 화면인데 정말 얄미웠음.....)

 

SpringBoot 콘솔창에 뜨는 에러문구

 

 

일반 접속자(비회원)면 기본 인덱스 화면으로 들어갈 수 있게 해뒀는데 왜 권한에러가 뜨나...싶어서 에러문구를 살펴보는데 definer/invoker of view lack rights to use them 이 문구가 눈에 들어왔다.

 

뒤져보니 mysql은 view는 table과 다르게 또 권한 (조회할 유저 설정)설정이 필요한것 같았고, 인덱스 화면에서 해당 뷰 데이터에 접속하려는데 권한이 없음 → 데이터 접근 불가한 유저라 에러화면에서 무한루프로 돌고있다고 추측했다.

 

원래 DB를 확인해보니 view 쿼리문에 definer가 기본으로 들어가있었다

 

 

이걸 보고 view를 열어봤더니 세상에 Definer가 왜 저모양이죠..? 나는 분명 데이터를 그대로 import했는데.....

 

왜 섞여있니..?

 

나는 root ID로접속했으므로 중간중간 root@%로 설정된 view를 열어보면 데이터가 잘 보이지만, octap@%로 된 뷰들은 접속하면, (속성은 그대로 잘 들고왔는데) Data가 아래와 같이 invalid table or column or function or definer/invoker of lack rights to use them. 으로 나온다.... 그렇담 뭐다? Definer를 수정해줘야 할 것 같다!

뷰에서 데이터를 볼 수가없으니 화면에도 뿌릴수가 없다...

 

 

그리고 방법을 알려주는 티스토리를 발견. 뒤져보니 ALTER를 통해서 해당 내용을 수정할 수는 있는데, 문제는 as 뒤에 뷰 생성 쿼리를 그대로 다 붙여줘야 한다는 것... 이게 무슨 노가다인지...ㅠㅠ 말이 alter지 그냥 새로 만드는거나 다름없었다. 한번에 바꿀수 없나 싶었는데 이러나저러나 쿼리문이 붙는걸 보고 걍..그냥..다...수기로 돌렸다.....

 

 

mysql view definer 수정

A 서버에서 디비를 그대로 백업해서 B 서버로 옮겨 왔다.하필이면 재수없게 view가 있다.definer가 A 서버에서 쓰던 계정(test@localhost)이 등록되어 있었다.그래서 지금 B 서버에서 testtest@localhost 계정

zzins.tistory.com

 

 

중간에 안뜨는 뷰만 내비두고 다 바꿔줬더니 프로젝트가 정상작동한다!

 

 

설치부터 디비연결까지 총 4시간걸렸다.. 원래 다른 분께서 dump하라고 알려주셨는데 어쩌다보니 db만 그냥 export해서 import처리만 하게 됐음. (사실 dump의 개념을 잘 모르겠어서 일단 내가 할 수 있는 방법으로 진행한것도 있었다)

 

이제 자잘한 설정만 보완하고 마무리해야겠다. 덕분에 이번 연휴는 마음편히 쉴 수 있게 됐다!

좋은 추리시간이었다...😅  정말 공부할건 한가득이구나.

 

플젝할땐 내꺼하기 바빠서 다른사람 코드를 깊이 들여다보질 못했는데 (심지어 난 갖다쓸일도 없었음) 복기한다고 열어보다보니 흥미로운 부분들이 보인다. 

 

<select id="similarPersonalityFindAll" resultType="MissionView">
    select * from
    mission_view
    where
    end_date>=curdate()
    and personality_id = 1
    order by 
    rand() 
    limit 4
</select>

 

 

이 부분은 인덱스 화면에서 비회원을 대상으로 보여주기위해, 쿼리문을 세팅해둔 부분인데, 다른건 다 알겠지만 order by 구문이 이해가 안갔다.

 

limit 4라서 4개만 뽑겠다는건 알겠고, 자바의 랜덤함수처럼 rand()도 랜덤함수 같은데 order by rand() limit 4는 그럼 무작위로 정렬해서 4개만 뽑는다는건가..? 했더니 정답.

 

이렇게 조합해서 쓰는건가 했더니 무작위로 추출할때 'order by rand()' 가 하나의 구문으로 쓰인다고 한다.

실행할때마다 다른값을 반환하는것, 그리고 0~1사이 이기 때문에 SELECT FLOOR(RAND() * 100) + 1; 이런식으로 가공을 해주어야 1~100까지의 난수가 나온다는것도 자바의 랜덤객체 특성과 동일하다. 😁

아니... 이거때문에 전부터 꽤 오래 고민했는데 너무 허무하게 처리돼서 조금 당황스럽다.

(* 아직 여러가지 테스트를 해본건 아니라 나중에 오류 터질수 있음 주의)

 

이 문제의 포인트는, DB에 HTML이 들어갔다가 나오면 한줄 출력이 된다는데 있다...

 

textarea 박스에서 엔터키를 쳐서 작성했음에도 불구하고 다시 불러오면 이렇게 한줄로 나온다

 


 

인터넷을 찾아봤을때 대부분의 의견은 이랬다.

-- 일단 이 작업의 전제는 textarea에서 wrap:hard로 저장해야 한다는 것. 띄어쓰기와 개행을 그대로 입력해주는 속성이다. 만약 이 속성없이 그냥 저장하면 css에서 아무리 줄바꿈 해봤자 적용이 안됨!

 

코딩교육 티씨피스쿨

4차산업혁명, 코딩교육, 소프트웨어교육, 코딩기초, SW코딩, 기초코딩부터 자바 파이썬 등

tcpschool.com

 

 

1. 컨트롤러에서 replace로 ('\r\n","<br>")로 교체해서 데이터베이스에 저장하기. 단 사용자 수정화면에선 다시 반대로 바꿔줘야한다. ▶ 이렇게 하면 화면에서는 타임리프로 th:utext를 통해 꺼내면 알아서 개행처리된다

(*데이터베이스에서 <br>태그를 껴서 문장을 넣어놓고 th:utext로 뽑아보면 바로 확인가능)

 

2. 넣을때도 <textrea>, 꺼낼때도 <textarea>를 쓴다 (*해보진 않았음!)

 

3. 자바스크립트+정규식을 쓴다

 

4. (내가 한 방법) 꺼낼때 css속성인 white-space를 활용한다

 

컨트롤러에서 어떻게 처리해야하나...고민하며 검색하던 중에, css에서 줄바꿈 처리 속성인 white-space를 가지고 적용하면 된다는 이야기를 우연히 보게됐고 , 아니 이게 줄바꿈 처리하는 속성은 맞긴 맞는데 저장할때부터 잘만 저장해주면 이거 하나로 해결되는게 정말일까? 싶어서 헐레벌떡 해봤다.

 

 

 

▼ 처음봤을땐 이해못해서 지나쳤다가 (왜냐면 css만 한번 적용했었는데 안되길래 걍 안되는줄 알았음) wrap:hard 로 속성 저장 가능한걸 보고 다시보니 그걸 그대로 꺼내면되는구나!!!! 하고 이해한 글

 

textarea로 입력 받은 글을 공백과 줄바꿈을 살려 출력하기

HTML에디터가 아닌 일반 textarea 로 입력 받은 글을 페이지에 출력해야 할 경우가 많이 있습니다. 출력시에는 보통 JSP 라면 형태로 출력합니다. 이 경우 html 태그들은 escape 되어서 태그가 화면에 그

offbyone.tistory.com

 

 

white-space - CSS: Cascading Style Sheets | MDN

CSS white-space 속성은 요소가 공백 문자를 처리하는 법을 지정합니다.

developer.mozilla.org

 

 

브랜치 파고 남의 코드 쳐들어가서(허락받음) 끼워넣기

style="white-space:pre;"

style="white-space:pre-line;"


//---------------
<a th:href="@{/member/feed/log(s=*{missionLogId})}" th:if="*{logContents!=null}">
    <p style="white-space:pre-line;" class="contents" th:text="*{logContents}">미션 로그 내용 출력</p>
</a>

 

👉 값을 불러오는 박스에만 해당되면 되기 때문에 html에서 직접 설정했다.

 

 

그리고 매 우 잘 됨!

조합해서 시도해볼 생각 안하고 그냥 컨트롤러에서 replace 썼으면 초가삼간 태울뻔했다. 근데 또 그렇게 하는 이유가 있을테니, 나중에 오류생기면 그렇게 바꿀수도 있겠지 ...😂 제발 생기지 말거라.

 

 

처음엔 pre로 설정했는데, 반응형 확인하느라 모바일을 봤더니, 폭이 축소되어도 한 문장이 길면 튀어나가있었다.

mdn보고 자동줄바꿈이 되는 pre-line으로 처리해줌.

 

테스트 성공하자마자 정신 똑디 차리고 textarea가 있는 html, js 랑 출력값인 모든 html, js에 꽂아넣었다! 

 


 

 

웹화면 아주 잘나오고요
모바일화면

 

👉 기존에 엔터키를 넣어서 작성했던 글도, 모바일 화면에서 아주 잘 출력되는걸 볼 수 있음!

 

👉  위 모바일 화면을 웹으로 바꾸면 스크롤이 생기게 해놨는데, 그 안에서 줄바꿈 매우 잘 됩니다 😉

 

 

숙원사업 같은 애였는데 드디어 해결해서 속시원하다. 피드가 뷰 of 뷰인데 이런거 하나 신경안썼단 소리 듣고싶지 않았음 ㅠ ㅠ

 

잘 배운김에 textarea속성도 한번 쭉 읽어보았다! 속성을 빨리알았더라면 고민하는 시간도 줄었을텐데 ..!

 

<textarea>: The Textarea element - HTML: HyperText Markup Language | MDN

The <textarea> HTML element represents a multi-line plain-text editing control, useful when you want to allow users to enter a sizeable amount of free-form text, for example a comment on a review or feedback form.

developer.mozilla.org

 

공지사항은 이미 SSR, CSR로 모두 해본 페이지이고, 하단 페이저 구현만 남았는데 여기서 고민이 됐다.

(*참고로 피드는 스크롤방식인데 공지사항에 페이저를 넣은건 디자인 할 때부터 의도적이었다! 가능한 여러가지 방법으로 페이지를 만들어보고 싶었음)

 

▶ 페이저를 게시물 개수에 따라 동적으로 생성시키고 싶다

▶  페이저만 CSR로 만들것인가 리스트도 CSR로 만들것인가?

▶ CSR의 단점인 번쩍거리는 특징때문에 SSR로 첫페이지를 다 만들어오고 (페이저 생성조건도 다 처리해서 들고오는걸로) 이벤트로 넘어가게 할 것인가?

👉 이때 선생님께서 SSR, CSR방식에 모두 익숙해졌으니 Vue.js 라이브러리를 사용해서 완전한 클라이언트 생성방식을 써보라고 하셨다. (*SPA 방식. 시작하기전에 잠시 의미도 찾아보았다!)

 

 

 

[WEB] SPA 란?

SPA 중심에는 대표적인 라이브러리/프론트프레임워크 React, Vue/Angular가 있다.

linked2ev.github.io

 

 

😂 역시나 수업시간 외에 처음으로 해본 방식이라 다사다난했다. 마지막즈음 가니 어떻게 서로 연결시키는지 조금 감이 와서 페이저 구현이 오히려 쉬웠다!

😂 동시에 느낀 것. 이래서 브랜치가 필요하구나... 그냥 내 브랜치에서 파일만 복사해서 작업하려다가 혹시 모르니까 브랜치 하나 더 파서 거기서만 작업했다. 버전관리라는게 이런느낌이구만.

 

 


 

 

1. 이 문구는 무슨뜻인가 

You are running a development build of Vue.
Make sure to use the production build (*.prod.js) when deploying for production.

 

이 메시지는 Vue.js 애플리케이션을 개발 환경에서 실행할 때 나타날 수 있는 경고 메시지입니다. Vue.js는 개발 버전과 프로덕션(배포) 버전으로 나누어집니다. 개발 버전은 개발 중 디버깅 및 개발 툴의 지원을 위해 추가 로깅 및 경고를 포함하고 있습니다. 프로덕션 버전은 더 최적화되고 경량화되어 성능이 더 좋으며, 애플리케이션 배포에 사용됩니다.

해당 메시지는 개발 환경에서 Vue.js 개발 버전을 사용하고 있을 때 나타나며, 실제로 애플리케이션을 프로덕션 환경에 배포할 때는 Vue.js 프로덕션 버전을 사용하는 것을 권장합니다. 프로덕션 버전은 추가적인 디버깅 정보와 경고를 제거하여 애플리케이션을 더 가볍게 만듭니다.

프로덕션 버전의 Vue.js는 .prod.js 확장자를 가진 파일로 제공됩니다. 이 파일을 사용하면 위의 경고 메시지를 제거하고 애플리케이션을 프로덕션 환경에 배포할 수 있습니다.

따라서 개발 중에는 개발 버전을 사용하고, 애플리케이션을 배포할 때는 Vue.js 프로덕션 버전을 사용하는 것이 좋습니다.

👉 수업시간엔 왜 눈치 못챘는지 모르겠는데, 이제서야 눈에 들어와서 찾아보니 개발용과 배포용을 안내해주는 문구였음.

 

2. id를 못찾아서 mount가 안된다면 잊지말고 defer 처리하기

 

👉 이 부분을 까먹고있었어서 id에 오타가 있나 싶었는데 파일을 읽을수 있도록 처리를 안해줘서 그런것.........으이그

 

 

 

3. 원하는 형태로 날짜 포맷하기 (compute와 메서드 사용)

1. 필터사용

filters:에 포맷하고 싶은 형태를 정의해서 쓸 수 있다는데, 나는 자꾸 global에 필터명이 등록되지 않았다는 알림이 나왔다. 일단 패스.

2. 다른사람들이 쓴다는 라이브러리 (dayJs)

vue도 처음 써보는 마당에 굳이 다른 라이브러리로 고민하고 싶지 않아서 패스. 그리고 필터 사용에 관한 포스팅 쓰신분이, 어렵지도 않은거 그냥 필터 만들어 쓰신다기에, 나도 기존에 써둔 날짜포맷 코드를 살리기로 했다.

3. computed

vue에서 제공하는 속성으로, 컴포넌트에서 계산된 속성을 정의하는데 사용한다고 한다.(계산결과 캐시/ 데이터변환 / 필터링/ get함수 포함)

정의된 속성은 해당 데이터가 변경될때 다시 계산되는데, 나는 for문에 들어가는 데이터를 반영시켰으므로 필터링을 여기서 부여할 수 있었다.

 

 

	data() {
		return {
			fixed: [],
			list: [],
		}
	},
	computed: {
		formattedFixed() {
			return this.formattedDate(this.fixed);
		},
		formattedList() {
			return this.formattedDate(this.list)
		},
	},
	created() { // 문서가 처음 로드될때 생성할 부분은 create()에서 정의한다
		this.loadFixed();
		this.loadList();
	},
	methods: {
		//생략
		formattedDate(data) {
			return data.map(item => {
				let regDate_ = new Date(item.regDate);
				let year = regDate_.getFullYear();
				let month = regDate_.getMonth()+1;
				let date = regDate_.getDate();

				if (month < 10)
					month = '0' + month;

				if (date < 10)
					date = '0' + date;

				return year + '-' + month + '-' + date;
			});
		}
	}
//html
<ul class="info-box" v-for="(f,index) in fixed">
    <ul class="title">
        <li class="hold">[중요]</li>
        <li><a :href="'detail?n='+f.id"
                @click="fixedCountHandler(index)"><span>{{f.title}}</span></a>
        </li>
    </ul>
    <ul class="subinfo">
        <li>{{formattedFixed[index]}}</li> //날짜 입력 부분. index를 가지고 계산된 결과를 반영
        <li>조회수 <span>{{f.hit}}</span></li>
    </ul>
</ul>

👉 data.map을 사용하는 부분이 생소해서 찾아봤는데 아래와 같은 차이점이 있다고 한다.

 

  • map은 콜백합수의 반환값들로 이루어진 새로운 배열 반환(특정 프로퍼티 값만 들고오거나 배열 내 요소들을 모두 콜백의 반환값으로 받고 싶다)
  • filter는 콜백함수의 반환값들이 true인 요소로만 구성된 새로운 배열 반환 (조건에 충족하는 요소들을 배열로 만들고 싶다)

 

 

[Javascript] map와 filter 활용 예시

map과 filter의 활용 예시

velog.io

 

 

 

 

4. pager 생성하기

1. 같은 데이터 내 자료에 this.를 쓰는게 적응이 안된다.

자바에서는 알아서 판단이돼서 (?) 잘 썼는데 여기는 정신이 없어서 자꾸 놓치고 있다. 자스 많이 안해본 티가 여기서 나나요.

data() {
		return {
			fixed: [],
			list: [],
			pager:[],
		}
	},
	computed: {
		//생략
		},
	},
	created() {
		console.log("생성됨")
		this.loadFixed();
		this.loadList();
		this.countList(this.pager); //여기에 pager라고만 썼다가 왜 연동이 안되지(?)함
	},
    methods: {
		//다른 메서드 생략
		async countList() {
			let response = await fetch(`/api/notices`)
			let listCount = await response.json();
			console.log(listCount);

			let page = 0;
			if (listCount % 5 > 0)
				page = parseInt(listCount / 5) + 1
			else
				page = parseInt(listCount / 5)

			//마지막 페이지 업데이트
			this.lastPage = page;

			//pager[] 업데이트
			for (let i = 0; i < page; i++) {
				this.pager[i] = i + 1;
				/*this.pager.push(i+1);*/
			}
		},

 

 

 

2. Vue.set대신에 this.pager에 대입하면 안되나?

this.pager[i] = i + 1과 Vue.set(this.pager, i, i + 1)는 데이터에 새로운 속성을 추가하는 데 사용됩니다. Vue.js 내부적으로 데이터의 변경을 감지하기 위해 리액티브 시스템을 사용하고 있으며, 데이터 변경을 추적하고 화면을 다시 렌더링하는 데 필요한 업데이트를 수행합니다.

this.pager[i] = i + 1는 일반적인 JavaScript 코드로, Vue의 리액티브 시스템에 대한 인식이 없기 때문에 Vue가 이 변경을 추적하지 못할 수 있습니다. 따라서 새로운 속성을 추가할 때는 Vue.set을 사용하여 Vue에게 이 변경 사실을 알려야 합니다.

따라서 데이터 변경을 Vue에게 알리기 위해 Vue.set 또는 this.$set을 사용하는 것이 좋습니다. 이렇게 하면 Vue가 변경 사항을 정확하게 추적하고 컴포넌트를 업데이트할 수 있습니다.

👉 변경될때마다 적용시키는 (리스너같은) 무언가가 필요하다는것 같다고 생각함.

 

 

반응형에 대해 깊이 알아보기 — Vue.js

Vue.js - 프로그레시브 자바스크립트 프레임워크

v2.ko.vuejs.org

콘솔에는 분명 입력된걸로 나오거든요!!!

vue.set을 쓰지않고 for문으로만 배열 업데이트를 했을때, 화면에서 2까지 입력된건 반영이 되어서 페이저 디자인은 2번 반복 되었다! 다만 안에 있는 숫자 1,2가 출력이 안되는 상황. 위에 있는 data:{pager:[]}에 반영이 안되고 있는것 같았음. 함수 내에서 pager자체에 1,2가 들어간건 맞는데 말이야 (콘솔내용처럼)

 

도대체 this.$set을 어떻게 꾸려야 할까 엄청 고민했는데 이것저것 하다보니 원래 했던 this.pager[i]=i+1도 되는게 맞고 this.pager.push(i+1)로 collection을 활용하는것도 맞았다. 결국 잘 출력됨 ^^;;;

 

문제는 내가 출력하는 방법에 있었다.

<li v-for="(p,index) in pager"><a class="pager selected" href="">{{p}}</a></li>

반복문으로 pager를 뽑아서 뽑아온 키 p의 값에 접근하므로 {{p}} 자체가 값으로 바인딩 되는건데, 순간 착각해서 {{p.index}}로 써놓은게 함정이었다. index는 js에서 활용할 필요가 있을때 뽑아쓰려고 설정한다 생각해야함. (각 클릭값을 눌러서 페이지를 변경해야하기때문에 결과적으로 있긴 있어야 한다)

 

 

 

3. 클릭이벤트와 메서드 연동하기

👉 이쯤되니까 필요할때마다 data에 뭘 선언하고 어떻게 뷰와 연결하는지 좀 더 감이 왔다. 타임리프처럼 it문이나 classappend안쓰고 선택된 페이저의 css class를 어떻게 끼우나 했는데, 돌아가는게 보이기 시작하니까 인덱스 맞추기가 쉬워졌음! 며칠 뒤에도 기억할 수 있을지 의문이라 기록해두긴 하지만 ^^..

 

 


 

결과물!

👉 타임리프 쫙쫙 빼고 html을 최대한 뼈대로 남겨둘 수 있어서 좋았다.  다만 화면이 번쩍거리는게 SSR방식에 비해서 너무 신경쓰인다.

 

현재까지 구현기능

1. 중요공지 3개까지 상단고정됨 (관리자페이지 버튼과 연동)

2. 즉, 페이저 버튼 누를때마다 일반공지 리스트만 변경됨

3. 세모 아이콘은 맨앞, 맨뒷페이지로 이동하고, 숫자는 게시글 개수에 맞춰서 생성됨. (현재 일반공지 글 8개라 2페이지까지 생성)

 

추가할 기능+)

1. CSR 특유의 번쩍거림을 방지하기 위해 로딩화면에 뜨는 스피너를 구현해볼 예정 

(참고블로그 https://hobbada.tistory.com/5)

2. 페이저를 5개까지 끊어서 생성되도록 하기. 그럼 아이콘들 역할도 바껴야 할 듯!

 

 

+ Recent posts