11장 팔로잉 기능 구현에서는 팔로잉 하는 목록 페이지 팔로우 하고 있지 않는 목록 페이지 두 가지를 구현하였습니다. 이를 기반으로 인스타그램처럼 프로필을 카드기반으로 꾸미고 10-12 에서 구현한 무한 스크롤을 적용하였습니다. 프로필 페이지에서 구현한 프로필 카드를 목록에서 재활용하여 반복을 줄이고자 했습니다. 대부분의 기능은 구현하였습니다만, 강의의 내용만으로는 스스로 해결하기 힘든 점들이 있어 질문글을 파란색으로 남깁니다. 우선, 팔로잉 목록과 언팔로인 목록을 나눈 경우 언팔로잉 목록에서 내가 팔로잉 한지 모르고 검색한 경우 유저 목록이 검색되지 않는 불편함이 존재할 수 있습니다. 물론 인스타 그램의 경우 강사님께서 구현한 것처럼 탭을 나누어서 구현하였으니 이는 문제가 아닐 수 있습니다. 그러나 하기의 사진은 정확한 예시는 아닐 수 있으나 내 팔로워들 중 내 팔로잉 여부를 확인할 수 있는 페이지를 구현하고 싶습니다. 인스타 팔로워 페이지 인스타 팔로잉 페이지 따라서 팔로우하지 않는 목록 페이지를 유저 목록들이 모두 나열되면서 눈으로 팔로우 여부를 확인할 수 있는 페이지로 구현하고자 합니다. 하기는 현재 구현한 목록 페이지의 일부 입니다. 개인 프로필 페이지에서는 팔로잉 버튼 대신 edit 버튼이 활성화 됩니다. 해당 페이지를 구현하기 위해 11장에서 구현한 두 함수를 generic ListView를 상속받은 class 기반 뷰로 구현하였습니다. # views.py @method_decorator(login_required_hx, name="dispatch") class DiscoverListView(ListView): model = User paginate_by = 12 context_object_name="user_list" def get_queryset(self): qs = User.objects.all() user: User = self.request.user follow_relation = self.kwargs.get("follow_relation", False) # url로 부터 받는 인자 match follow_relation: case "following": qs = user.following_user_set.all() case "followed": qs = user.followed_user_set.all() # qs = User.objects.exclude(id__in=user.following_user_set.all()) qs = qs.exclude(id__in=[user.pk]).select_related("profile").prefetch_related("follower_user_set", "following_user_set") query = self.request.GET.get("query", "").strip() if query: qs = qs.filter( Q(username__icontains=query) | Q(full_name__icontains=query) | Q(email__icontains=query) ) return qs.order_by("username") def get_template_names(self) -> list[str]: # htmx로 요청이 들어오면 검색바 등 불필요한 위 아래 내용 제거 후 내용만 업데이트 if self.request.htmx: template_name = "accounts/_user_list.html" else: template_name = "accounts/user_list.html" return [template_name] def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) # is_follwer 는 html에서 follow 버튼을 구분하기 위한 인자 # context['is_follower'] = True if self.kwargs.get("follow_relation", False)=="following" else False qs = self.get_queryset() follow_dict = { q.username:self.request.user.is_follower(q) for q in qs } context['follow_dict'] = follow_dict query = self.request.GET.get("query", "").strip() context['query']=query return context 여기서 .prefetch_related("follower_user_set", "following_user_set") 을 통해 모델에서 구현한 팔로워와 팔로잉 유저 수를 불러오는 property를 templates에서 호출할 때 N+1 문제를 해결하고자 하였습니다. <!-- _user_profile_card.html --> {% load static %} <div class="card" style="border-radius: 15px;"> <div class="card-body p-4"> <div class="d-flex"> <div class="flex-shrink-0"> {% if user.profile.avatar %} <img src="{{user.profile.avatar.url}}" alt="{{user}}'s avatar" class="img-fluid" style="width: 180px; border-radius: 10px;"> {% else %} <img src="{% static 'images/profile-icon-design-free-vector.jpg' %}" alt="{{user}}'s avatar" class="img-fluid" style="width: 180px; border-radius: 10px;"> {% endif %} </div> <div class="flex-grow-1 ms-3"> <h5 class="mb-1" style="display:inline">{{ user.full_name }} </h5> {% if user.profile.team %} <h6 class="text-muted" style="display:inline">{{ user.profile.team }}</h6> {% endif %} <p class="mb-1 pb-1">{{ user.email }}</p> <div class="d-flex justify-content-start rounded-3 p-2 mb-2 bg-body-tertiary"> <div> <p class="small text-muted mb-1">Data</p> <p class="mb-0">TDA</p> </div> <div class="px-3"> <p class="small text-muted mb-1">Followers</p> <p class="mb-0">{{user.follower_count}}</p> </div> <div> <p class="small text-muted mb-1">Following</p> <p class="mb-0">{{user.following_count}}</p> </div> </div> <div class="d-flex pt-1"> {% if request.user == user %} <a href="{% url 'accounts:profile_edit' %}" class="btn btn-outline-primary btn-sm"> <i class="bi bi-pencil-square"></i> 프로필 수정 </a> {% else %} {% comment %} {% include "accounts/_user_follow.html" with username=user.username%} {% endcomment %} {% include "accounts/_user_follow.html" with is_follower=follow_dict.user.username username=user.username%} {% endif %} </div> </div> </div> </div> </div> 프로필 카드 내에서 팔로워와 팔로잉 수를 보여주기 위해 접근한 제 방식이 맞는지가 궁금합니다. models.py 에서 구현한 follower_count 또는 following_count 를 template에서 호출하는 방식이 일반적인 접근 방식인가요? 그 다음 제가 위에서 구현하고자 한 유저별 팔로우 여부를 get_context_data 에서 follow_dict 라는 변수명으로 저장하여 하기와 같이 template에 넘겨주었습니다. <!--user_follow.html--> {% include "core/_messages_as_event.html" %} {% load keyvalue %} {% if follow_dict|keyvalue:username %} <a href="#" hx-post="{% url 'accounts:user_unfollow' username %}" hx-swap="outerHTML" class="btn btn-sm btn-primary"> <i class="bi bi-person-check-fill"></i> 팔로잉 중 </a> {% else %} <a href="#" hx-post="{% url 'accounts:user_follow' username %}" hx-swap="outerHTML" class="btn btn-sm btn-secondary"> <i class="bi bi-person-add"></i> 팔로잉 하기 </a> {% endif %} dictionary 키 값으로 변수를 입력하는 것이 django template에서 지원되지 않기 때문에 하기의 함수를 등록하여 활용하였습니다. register = template.Library() @register.filter def keyvalue(dict, key): return dict[key] 결과물은 구현한 페이지처럼 잘 보여지지만, 두 가지 문제가 존재합니다. 확실치 않지만 하기의 코드에서 N+1 오류가 발생하는 것으로 보입니다. follow_dict = { q.username:self.request.user.is_follower(q) for q in qs } 팔로잉 혹은 언팔로잉 요청 시 500 서버 에러가 발생합니다. 아마도 follow_dict가 요청 시에 업데이트 되지 않아 발생하는 오류로 보입니다. 이러한 점 때문에 인스타그램에서도 따로 탭을 두어서 관리하는가 싶기도 합니다만 이를 해결할 방법이 있을까요? 어렵다면 팔로워 목록에서 내가 팔로잉 하는지 여부를 표기하는 인스타그램 페이지는 어떻게 구현해야 할까요?
안녕하세요.. 최근에 강의를 수강하고 있습니다. 2-4강을 따라서 테스트하고 있습니다. 그런데 총 10개의 링크를 가져와서 puppeteer를 이용해서 평점값을 가져오는데, 중간에 하나의 결과를 저장하지 않는 오류가 발생합니다. 왜 그럴까요? 처음에는 인덱스 번호 2번이 undefined 되더니, 이번에는 인덱스 7번이 생성이 안되어서 cs 파일 생성시 오류가 발생합니다. 이런 현상은 왜 발생할까요?
어떨 때 useRef를 사용하고 어떨 때 useState를 사용하는지 기준을 모르겠어요. 리렌더링 즉 화면 UI가 변할 때usestate를 사용한다고 생각해도 될까요? Input 값이 비어있으면 폼을 제출 못하고 focus()하는 메서드를 사용할 때 포커스도 화면 UI가 변경된 것일텐데, useref를 사용하는 거 같더라구요 그렇다면 리액트가 변화를 감지해야할 때는 usestate를 사용하고 감지하지 않아도 되는 경우에는 useref를 사용한다고 생각해야할까요?? 마지막으로 keyframe애니메이션을 사용하려면 UI가 변경되었기때문에 usestate로 사용해야하는 걸까요? 아니면 리액트가 그 변화를 감지하지 않아도 되기때문에 useref를 사용해야하는 걸까요?ㅠㅠ 정말 헷갈립니다
안녕하세요! 카운터앱 구현하기를 조금 다르게 코드를 짜보았는데, 커스텀훅을 활용법에 익숙하지 않아서, 이렇게 구현하는 방법이 적절한지 감이 안와요.. 바쁘신데 죄송하지만 아래와 같은 커스텀훅 구현방식의 장단점에 대해서 알려주시면 감사하겠습니다! 먼저 useCount.jsx를 아래와 같이 작성하였구요 import { useState } from 'react'; function useCount() { const [num, setNum] = useState(0); const onClinkCount = (e) => { setNum(num + Number(e.target.value)) } return [num, onClinkCount]; } export default useCount; 아래는 넘버 조작 부분인 Controller.jsx const Controller = ({ onClinkCount }) => { return ( <div> <button onClick={onClinkCount} value="-1">-1</button> <button onClick={onClinkCount} value="-10">-10</button> <button onClick={onClinkCount} value="-100">-100</button> <button onClick={onClinkCount} value="+100">+100</button> <button onClick={onClinkCount} value="+10">+10</button> <button onClick={onClinkCount} value="+1">+1</button> </div> ) } export default Controller 카운트 넘버 표시부분인 Viewer.jsx const Viewer = ({ num }) => { return ( <div> <div>현재 카운트:</div> <h1>{num}</h1> </div> ) } export default Viewer 그리고 부모 컴포넌트인 App.jsx 입니다. import "./App.css" import Controller from './components/Controller' import Viewer from './components/Viewer' import useCount from './hooks/useCount' function App() { const [num, onClinkCount] = useCount(); return ( <div className='App'> <h1>Simple Counter</h1> <section> <Viewer num={num} /> </section> <section> <Controller onClinkCount={onClinkCount} /> </section> </div> ) } export default App 🚨 아래의 가이드라인을 꼭 읽고 질문을 올려주시기 바랍니다 🚨 질문 하시기 전에 꼭 확인해주세요 - 질문 전 구글에 먼저 검색해보세요 (답변을 기다리는 시간을 아낄 수 있습니다) - 코드에 오타가 없는지 면밀히 체크해보세요 (Date와 Data를 많이 헷갈리십니다) - 이전에 올린 질문에 달린 답변들에 꼭 반응해주세요 (질문에 대한 답변만 받으시고 쌩 가시면 속상해요 😢) 질문 하실때 꼭 확인하세요 - 제목만 보고도 무슨 문제가 있는지 대충 알 수 있도록 자세한 제목을 정해주세요 (단순 단어 X) - 질문의 배경정보를 제공해주세요 (이 문제가 언제 어떻게 발생했고 어디까지 시도해보셨는지) - 문제를 재현하도록 코드샌드박스나 깃허브 링크로 전달해주세요 (프로젝트 코드에서 문제가 발생할 경우) - 답변이 달렸다면 꼭 확인하고 반응을 남겨주세요 - 강의의 몇 분 몇 초 관련 질문인지 알려주세요! - 서로 예의를 지키며 존중하는 문화를 만들어가요. - 인프런 서비스 운영 관련 문의는 1:1 문의하기를 이용해주세요.
안녕하세요 선생님! 예전에 선생님 강의 청첩장강의랑 flex,grid, 기타 강의들 구매하고 들었던 학생입니다. 선생님은 메인개발분야가 뭔지 궁금합니당. 또 리액트 사용하시는지도 궁금합니다! 질문과는 무관한 여담이지만,, 최근 리액트로 프로젝트하면서 애니메이션 편하게 구현하기위해서 여러 라이브러리를 사용하고 또 gsap관련해서도 찾아보고 했었는데.. gsap로 이렇게 손쉽게 인터랙티브 애니메이션을 구현할 수있다고? 하면서 혁신이다! 라고 생각했었는데 보니깐 자체 서비스에서 결제 같은 기능이있을경우 (넷플릭스같은) gsap 프리미엄을 결제해서 사용해야하더라구요.. 그것도 연간 구독 시스템이고, 코딩하는 개발자 수에따라서 개별로 구매..ㅎㄷㄷ 그래서 돌고돌아 다시 선생님 곁으로왔습니다. 이 강의 듣다가 개인적인 일들이 겹쳐서 중간하차했었는데 다시 처음부터 차근차근 수강하고 복습해서 마스터해보려고합니닷.. 질문 요약 1: 선생님 메인분야가 뭐에요? 2: gsap에 관해서 의견이궁금합니다.
presigned URL를 실무에서 사용할려다가 지적받은게 업로드 권한을 클라이언트에 내려주고 횟수제한을 거는게 아니라 유효시간동안은 해당 url로 언제든지 업로드할 수 있어서 추후에 악성이미지 같은걸 업로드하면 보안적인 이슈가 있지않냐라고 말을 들었어요. aws의 s3에서는 횟수제한 같은건 걸 수 없어서 일단 formdata로 올려보내고 multer 사용해서 받아서 처리하긴 했는데 강사님 경우에는 presigned URL할 때 어떻게 조치를 하시나요?
해당 회차까지 하고 나니 Product도 확인해 볼겸 들어가보니 사진도 안나오고 pname도 undefied로 나오네요 server 쪽에서 Authorization 의 값이 null이라고 뜨는게 이유 인거 같은데 제가 잘못 한게 있을까요? 아니면 뒤의 회차에서 해결하는 방법이 제시가 되는건가요??
안녕하세요, 강의 다 듣고 혼자서 이것저것 보면서 만들다가 궁금증이 생겨서 문의 드려봅니다. vuex 에서 helper 기능을 사용 및 axios 통해서 데이터를 받아서 리스트를 뿌려보는 예제를 혼자 만들어 보았는데, 원래 axios 기능을 사용하면 then,catch, 등으로 resp 및 에러체크를 하는데 vuex actions 에서 동작시킬 때 async/await를 사용해서 코드작성할 때 이렇게 작성해도 문제가 없을까요?? mutations : { setUsers(state, data){ state.users.push(data); } }, actions : { async getUsers({commit}){ let resp = ''; try{ resp = await axios.get('https://jsonplaceholder.typicode.com/todos'); for(var i=0; i<resp.data.length; i++){ const data = resp.data[i]; this.state.users.push(data); } }catch(e){ console.log(e); } commit("setUsers", resp.data); } }
안녕하세요. 강좌 잘 보고 있습니다. 다름이 아니라 vercel 에 onebite-books-server 를 배포하였습니다. 로컬에서는 작동을 확인하였습니다. 첫 화면은 잘 나오는데 /api 폴더로 접근시 화면에 swagger 가출력이 안되고 있네요. ㅠㅠ; vercel 로그는 정상 배포 되었구요. 첫화면은 잘 나옵니다. 혹시 환경변수를 셋팅해야 하는가 해서 검토해 보았지만 필요없으시다고 하셔서 그냥 두었습니다. 에러 메시지는 콘솔창에 Failed to load resource: the server responded with a status of 404 () 이런 메시지가 여러개 출력 중입니다. 확인좀 부탁드립니다. 감사합니다.
16-02-infinite-scroller 강의 중에 이해가 가지 않는 코드가 한줄 있어서 질문드립니다! InfiniteScroll에 pageStart 옵션이 걸려있는데, 이게 어디에 영향을 주는건가요? 초기 페이지 번호라기에는 이 옵션을 999999 처럼 의미 없는 값을 줘도 문제 없이 작동하고 그러네요.. 딱히 의미가 없는 코드 같은데 pageStart라는게 어떤식으로 작동되는건지가 좀 궁금합니다..
3-3강에 레이아웃 구성하기 강의 앞부분이 잘린거같은데 저만 그런가요..? 학습에 관련된 질문만 해주세요. 질문은 상세하게 무엇이 궁금한지 작성해주세요. 질문은 '마크다운'을 사용하여 할 수 있습니다. 유사한 질문이 있었는지 살펴보고 질문 해주세요. 부담갖지 말고 강의에서 궁금하신 점 전부 질문해주세요 :)