묻고 답해요
161만명의 커뮤니티!! 함께 토론해봐요.
인프런 TOP Writers
-
미해결입문자를 위한, HTML&CSS 웹 개발 입문
저장시 줄바꿈
이상하게 선생님처럼 한줄로 태그랑 컨텐츠가 뜨는게 아니라 노트북 화면도 충분한데 저장하면 막 줄바꿈이 되는 경우들이 생기는데 뭐가 문제일까요 ㅠㅠ
-
해결됨[리뉴얼] React로 NodeBird SNS 만들기
`<List header={header}` 에서 header 같은 String 타입들도 useMemo 처리를 해주는 것이 좋은가요?
<List header={header} 에서 header 같은 String 타입들도 useMemo 처리를 해주는 것이 좋은가요?
-
미해결저작권 걱정없는 나만의 음악 만들기 - Studio One
라이선스
스튜디오원은 구매를 해야하는거죠? free 버전은 없지요?
-
미해결실전! 스프링 데이터 JPA
쿼리 힌트 Page 추가 예제 질문
[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예)[질문 내용]자료에 forCounting에 대한 설명을 적어주셨는데,"Count쿼리도 쿼리 힌트 적용"한다는 설명이@QueryHints의 forCounting값이 false이면 Count쿼리가 나가지 않는다는 말씀이신가요?[자료 위치]파일명 : "실전! 스프링 데이터 JPA -v2022-12-18.pdf"위치 : "JPA Hint & Lock" > "JPA Hint" > "쿼리 힌트 Page 추가 예제"영상 : "실전! 스프링 데이터 JPA" > "JPA Hint & Lock"자료에는 "쿼리 힌트 Page 추가 예제"가 있지만 영상 11:44 이후 Lock으로 바로 넘어가서 저는 잘 이해가 안가네요.. ㅠㅠ
-
미해결실전 프로젝트로 배우는 타입스크립트
소스질문
캡틴판교님이 인강 하시면서 작성하신 [코로나 상황판 프로젝트] 소스는 어디에 있나요?
-
미해결저작권 걱정없는 나만의 음악 만들기 - Studio One
사운드 출력 문제
PC에서 볼때 왜 왼쪽헤드폰으로만 나올까요? 모노로만 출력됩니다. (참고로 같은PC에서 유튜브 재생시 스테레오로 정상재생 됩니다)
-
미해결[개정판] 딥러닝 컴퓨터 비전 완벽 가이드
멀티 클래스 데이터를 coco data 포멧으로
제가 직접 모은 이미지 데이터 셋을 기반으로 MaskRCNN을 사용하여 object detection 과 segmentation 을 수행하려 합니다.올려주신 예제에는 CVAT을 통해서 Yolo3 포멧으로만 출력하는 예제가 있는데요, xml 이 아닌 COCO format json 으로 출력하려고 합니다.Balloon 강의를 보면 single class 만 인식하는 예제인데요, 멀티클래스를 학습하고 사용하려면 어떤 동영상 파일을 시청해야 하나요 아무리 찾아봐도 못 찾겠습니다. 너무 고통스럽네요 3일째입니다..Multi-class 학습가능한 MaskRCNN 모델 예제가 있는지Tensorflow2 버전에서 구동가능한지GPU를 자꾸 tesla4 를 잡는데 더 좋은 버전의 GPU를 제가 따로 지정할 수 있는지.. (프리미엄사용중입니다)감사합니다.
-
미해결[개정판] 딥러닝 컴퓨터 비전 완벽 가이드
코코 데이터셋트에 나오는 설명문장들...
강의 항상 잘 보고 있습니다. 또 항상 질문에 빠르고 상세한 답변해주셔서 감사드려요!갑자기 든 의문인데 COCO데이터 셋 웹사이트를 보면 이미지 설명하는 데이터들도 같이 있더군요..(이걸 image captioning 이라고 하나요?)이러한 정보들은 coco data json에서 어떻게 구현이 되나요? 이미지에 대한 설명 문장을 저도 자동으로 만들어 보고 싶네요.. 또한 input 을 문장으로 넣었을때 이미지를 출력해주는 것두요. 이러한 실습을 해보려면 선생님강의중에서 참고할 만한게 있을까요? 혹은 링크나 관련 내용 알려주시면 감사드리겠습니다.. COCO dataset 의 매력중에 하나가 image captioning 인것 같기도 한데.. 어떻게 사용하면 좋을까요? 감사합니다.
-
미해결Spring Cloud로 개발하는 마이크로서비스 애플리케이션(MSA)
RandomPort
랜덤 포트를 설정해도 Port가 0번으로 고정이 됩니다.한 개는 Intellij에서 Run 버튼을 이용해서 실행 시켰고한 개는 ./gradlew build 후 Intellij의 terminal에서java -jar first.jar를 이용해서 실행을 시켰습니다.instance-id도 추가를 해주었습니다. 혹시 몰라서 노트북 재시작까지 해보았는데도Eureka에서는 Port가 0번으로 고정이됩니다. 위의 사진은 Eureka 서버의 application.yml입니다.
-
미해결파이썬/장고 웹서비스 개발 완벽 가이드 with 리액트
선수지식 관련 질문입니다.
선생님 안녕하세요강의를 수강하기 전에 선수지식 관련해서 질문드립니다. 파이썬은 비교적 능숙하게 사용하고, 다른 장고 강의를 몇개 들어서, 기본적인 작동 원리는 이해한다고 생각합니다.다만, 리액트에 대한 지식이 전무하고 아주 기초적인 JS 지식만 있는 상태인데, 이 강의를 바로 들어도 무리가 없을까요?목표는 풀스택 개발자 라기 보다는, 장고에 대한 보다 깊은 이해 입니다. 즉, 리액트를 자유자재로 다루기보다는, 리액트가 어떻게 돌아가는지만 "이해"하고, 그것보다는 리액트를 곁들인 "장고"가 어떻게 돌아가는지를 이해하는 것이 목표입니다
-
미해결따라하며 배우는 노드, 리액트 시리즈 - 레딧 사이트 만들기(NextJS)(Pages Router)
프론트준비생에게 노드익스프레스 필수 사항 인가요?
server 폴더 부분 익스프레스로 만드시는거프론트준비생에게도 필수 사항 일까요?궁금합니다
-
미해결Spring Cloud로 개발하는 마이크로서비스 애플리케이션(MSA)
Eureka LoadBalancing
안녕하세요 질문이 3가지가 있습니다.다음과 같이 Eureka에 서버가 2개 등록되어있으면 유레카에서 로드밸런싱을 해주는 건가요? Client -> GateWay로 요청 보냄 -> Eureka에 서버 정보 요청 -> Eureka는 Application name에 여러 개의 IP가 있을 시 로드밸런싱을 해서 GateWay에 서버 정보를 알려줌 -> Gateway가 Eureka에서 얻은 서버 정보로 요청을 보냄이 과정을 맞을까요?위와 같이 8081, 9091이 등록이 되어있지만 Gateway를 호출을 해보면 8081만 나오게 됩니다.실행 시킨 방법은 8081은 Intellij에서 Run 버튼을 이용해서 실행 시켰고 9091은 Edit Configuration을 이용해서 -Dserver.port=9091을 이용해서 실행시켰습니다.게이트웨이의 application.yml은 다음과 같습니다.first-service의 application.yml은 다음과 같습니다.8081은 잘 찍히는데9091은 잘 나오지 않습니다.application.yml에 설정된 값이 8081로 되어있어서 8081만 나오게 되는걸까요?
-
해결됨파이썬/장고로 웹채팅 서비스 만들기 (Feat. Channels) - 기본편
[해결된 줄 알았는데 안됨] 실습)초간단 Echo 구현 실행 오류...
ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡhttp:127.0.0.1:8000/echo 들어가면이렇게 나오기는 하는데!콘솔이랑 파이참의 터미널을 보면!강의랑 다르게 이런 오류가 뜨긴 합니다!파이참에서 더 정확하게이런 오류가 뜹니다! 또, ws.send를 해도... 음...사실 제 친구 컴퓨터랑 2대를 동시에 하고 있는데 나오는 오류는 동일합니다.맨 처음 pyenv install 3.10.4 였나 그것도 동일한 core 머시기 MSI오류떠서 pyenv 쓰지 않았고..
-
미해결[코드팩토리] [초급] Flutter 3.0 앱 개발 - 10개의 프로젝트로 오늘 초보 탈출!
오타로 인한 컬럼명 변경
category_color.dart 파일내 아래 부분 컬럼명을 최초에는hexcode 로 한뒤 main 소스까지 실행을 했었는데요강의를 따라하다보니 오타인지를 확인했습니다 TextColumn get hexCode => text()(); hexcode -> hexCode 로 변경 하면 잘될줄 알았는데 아래처럼 오류를 뱉더라구요[VERBOSE-2:dart_vm_initializer.cc(41)] Unhandled Exception: Exception: You've bumped the schema version for your drift database but didn't provide a strategy for schema updates. Please do that by adapting the migrations getter in your database class. DB 파일초기화 하거나 버전올리는 방법이 있을까요!?
-
미해결Spring Cloud로 개발하는 마이크로서비스 애플리케이션(MSA)
filter가 작동되지 않습니다.
- 강의에 있는 거 그대로 사용했는데 log가 뜨지를 않습니다. 디버그를 찍어보니 필터를 거치지 않는데 어떻게 해결을 해야될까요??- 요청은 정상적으로 가고 응답도 first-service / second-serivIce에 맞게 잘 가져옵니다.
-
미해결따라하며 배우는 리액트 A-Z[19버전 반영]
styled component hoisting 질문
안녕하세요, styled component 적용하는 강의에서 질문이 있습니다.const의 경우 hoisting이 선언부만 되는 걸로 알고 있는데, container,Iframe 등을 코드의 가장 밑부분에 작성하더라도 사용될 수 있는 이유가 있을까요??
-
미해결스프링 프레임워크는 내 손에 [스프1탄]
'2022.12.23 오후 5:22에 질문드린 추가질문드립니다 글'에 대한 선생님 답변에 대한 질문입니다.
안녕하세요 선생님. 먼저 BoardController입니다. @Controllerpublic class BoardController { @Autowired private BoardMapper boardmapper; // HandlerMapping @GetMapping("/") public String root() { return "redirect:/main"; } @GetMapping("/main") public String main(Model model) { List<Board> list = boardmapper.getLists(); model.addAttribute("list", list); return "main"; // /WEB-INF/views/main.jsp -> forward } @GetMapping("/boardWriteForm") public String boardWriteForm() { return "boardWriteForm"; } @PostMapping("/boardWrite") public String boardWrite(Board vo) { boardmapper.write(vo); return "redirect:/main"; } @GetMapping("/boardMore") public String boardMore(@RequestParam("idx") int idx, Model model) { //public String boardMore(int idx, Model model) { boardmapper.boardCount(idx); Board vo = boardmapper.boardMore(idx); model.addAttribute("vo", vo); return "boardMore"; } @GetMapping("/boardDelete/{idx}") public String boardDelete(@PathVariable("idx") int idx) { boardmapper.boardDelete(idx); return "redirect:/main"; } @GetMapping("/boardUpdate/{idx}") public String boardUpdateForm(@PathVariable("idx") int idx, Model model) { Board vo = boardmapper.boardMore(idx); model.addAttribute("vo", vo); return "boardUpdateForm"; } @PostMapping("/boardUpdate") public String boardUpdate(Board vo) { boardmapper.boardUpdate(vo); return "redirect:/boardMore"; }} 두번째로 main.jsp 입니다.<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%><%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%><%@taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions"%><!DOCTYPE html><html lang="en"><head><title>Bootstrap Example</title><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.2/dist/css/bootstrap.min.css" /><script src="https://cdn.jsdelivr.net/npm/jquery@3.6.1/dist/jquery.slim.min.js"></script><script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js"></script><script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.2/dist/js/bootstrap.bundle.min.js"></script></head><body> <jsp:include page="header.jsp" /> <div class="container"> <h2>게시판</h2> <table class="table table-bordered"> <thead> <tr> <th>번호</th> <th>제목</th> <th>작성자</th> <th>작성일</th> <th>조회수</th> </tr> </thead> <tbody> <c:forEach var="vo" items="${list}"> <tr> <td>${vo.idx}</td> <td><a href="boardMore?idx=${vo.idx}">${vo.title}</a></td> <td>${vo.writer}</td> <td>${fn:split(vo.indate, " ")[0]}</td> <td>${vo.count}</td> </tr> </c:forEach> <tr> <td colspan="5" align="right"><a href="boardWriteForm" class="btn btn-primary">게시글 작성</a></td> </tr> </tbody> </table> </div></body></html> 세번째로 boardMore.jsp입니다.<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%><%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%><%@taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions"%><% pageContext.setAttribute("newLineChar", "\n");%><!DOCTYPE html><html lang="en"><head><title>Bootstrap Example</title><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.2/dist/css/bootstrap.min.css" /><script src="https://cdn.jsdelivr.net/npm/jquery@3.6.1/dist/jquery.slim.min.js"></script><script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js"></script><script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.2/dist/js/bootstrap.bundle.min.js"></script></head><body> <jsp:include page="header.jsp" /> <div class="container"> <h2>게시글 상세</h2> <table class="table table-bordered"> <tbody> <tr> <td style="max-width: 50px">제목</td> <td>${vo.title}</td> </tr> <tr> <td style="max-width: 50px">내용</td> <td>${fn:replace(vo.content, newLineChar, "<br/>")}</td> </tr> <tr> <td style="max-width: 50px">작성자</td> <td>${vo.writer}</td> </tr> <tr> <td style="max-width: 50px">작성일</td> <td>${fn:split(vo.indate, " ")[0]}</td> </tr> <tr> <td style="max-width: 50px">조회수</td> <td>${vo.count}</td> </tr> <tr> <td colspan="2" align="right"><a href="boardUpdate/${vo.idx}" class="btn btn-primary">수정</a> <a href="boardDelete/${vo.idx}" class="btn btn-danger">삭제</a> <a href="main" class="btn btn-success">목록</a></td> </tr> </tbody> </table> </div></body></html> 네번째로 boardUpdateForm.jsp입니다.<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%><%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%><%@taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions"%><!DOCTYPE html><html lang="en"><head><title>Bootstrap Example</title><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.2/dist/css/bootstrap.min.css" /><script src="https://cdn.jsdelivr.net/npm/jquery@3.6.1/dist/jquery.slim.min.js"></script><script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js"></script><script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.2/dist/js/bootstrap.bundle.min.js"></script></head><body><!-- <nav class="navbar navbar-expand-md bg-dark navbar-dark"> <a class="navbar-brand" href="../main"><img style="max-width: 80px" src="../resources/images/logo.png" /></a> <ul class="nav navbar-nav navbar-right"> <li class="nav-item"><a class="nav-link" href="#">김명수님, 안녕하세요</a></li> <li class="nav-item"><a class="nav-link" href="#">회원정보수정</a></li> <li class="nav-item"><a class="nav-link" href="#">로그아웃</a></li> </ul> </nav> --> <br> <div class="container"> <h2>게시글 수정</h2> <form action="../boardUpdate" method="post"> <input type="hidden" name="idx" value="${vo.idx}" /> <table class="table table-bordered"> <tr> <td style="min-width: 100px;">제목</td> <td><input type="text" name="title" class="form-control" value="${vo.title}" required></td> </tr> <tr> <td>내용</td> <td><textarea name="content" class="form-control" rows="10" required>${vo.content}</textarea></td> </tr> <tr> <!-- 추후 세션으로 처리 --> <td>작성자</td> <td><input type="text" name="writer" class="form-control" value="${vo.writer}" readonly></td> </tr> <tr> <td colspan="2" align="right"> <button type="button" class="btn btn-danger" onclick="location.href='../boardMore'">취소</button> <button type="submit" class="btn btn-primary">수정완료</button> </td> </tr> </table> </form> </div></body></html> 마지막으로 header.jsp입니다.<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%><nav class="navbar navbar-expand-md bg-dark navbar-dark"> <a class="navbar-brand" href="main"><img style="max-width: 80px" src="resources/images/logo.png" /></a> <ul class="nav navbar-nav navbar-right"> <li class="nav-item"><a class="nav-link" href="#">김명수님, 안녕하세요</a> </li> <li class="nav-item"><a class="nav-link" href="#">회원정보수정</a></li> <li class="nav-item"><a class="nav-link" href="#">로그아웃</a></li> </ul></nav><br> header.jsp를 공통적으로 적용하면 다 잘 되는데 updateForm에서는 logo를 인식하지 못합니다. logo의 주소가 No mapping found for HTTP request with URI [/m01/boardUpdate/resources/images/logo.png] 이런식으로 나오게 됩니다.지금 올린 코드에서는 주석처리한 부분이 공통적으로 한 것이 아니고 그 페이지에만 해당하는 방식으로 했습니다. 공통적으로 처리하는 방법 없을까요?<!-- <nav class="navbar navbar-expand-md bg-dark navbar-dark"> <a class="navbar-brand" href="../main"><img style="max-width: 80px" src="../resources/images/logo.png" /></a> <ul class="nav navbar-nav navbar-right"> <li class="nav-item"><a class="nav-link" href="#">김명수님, 안녕하세요</a></li> <li class="nav-item"><a class="nav-link" href="#">회원정보수정</a></li> <li class="nav-item"><a class="nav-link" href="#">로그아웃</a></li> </ul> </nav> --> 그리고 updateForm에서 취소버튼을 누르나 제출버튼을 누르나 HTTP 상태 400 – 잘못된 요청타입 상태 보고메시지 Required int parameter 'idx' is not present설명 클라이언트 오류로서 인지된 어떤 문제로 인하여, 서버가 해당 요청을 처리할 수 없거나, 처리하지 않을 것입니다. (예: 잘못된 요청 문법, 유효하지 않은 요청 메시지 framing, 또는 신뢰할 수 없는 요청 라우팅).이렇게 나오는 것도 왜 그런지 모르겠습니다... public String boardMore(@RequestParam("idx") int idx, Model model) { //public String boardMore(int idx, Model model) { 컨트롤러에서 이 부분은 int idx가 한개이므로 위나 아래나 다 가능한 것 아닌가요? 아래것으로 하면 오류가 java.lang.IllegalStateException: Optional int parameter 'idx' is not present but cannot be translated into a null value due to being declared as a primitive type. Consider declaring it as object wrapper for the corresponding primitive type.이런 오류가 납니다.그런데 오류가 나고 나서 다시 목록으로 가 보면 수정은 완료되어 있습니다. DB에 반영은 됩니다. 오류를 고치고자 선생님 올려주신 github 보고 계속 해봐도 도저히 안 됩니다. 어떻게 고쳐야 할까요??
-
미해결풀스택을 위한 도커와 최신 서버 기술(리눅스, nginx, AWS, HTTPS, 배포까지) [풀스택 Part3]
RUN 과 ENTRYPOINT 의 차이점이 궁금합니다!
안녕하십니까 강사님!좋은 강의 감사합니다!ENTRYPOINT와 RUN 모두 내부 콘솔에서 명령을 주는 옵션인데 어떨때는 RUN을 쓰고 어떨때는 ENTRYPOINT를 사용하는지 궁금합니다!감사합니다!
-
미해결스프링 시큐리티 OAuth2
Spring Boot 를 이용해 JWT + Social 로그인 처리 질문 있습니다.
안녕하세요. 우선 좋은 강의 제공 해주셔서 감사드립니다 :) 이번에 회사에서 신규 프로젝트로Client Server (Vue, React, Android, IOS)Back Server (Spring Boot)해당 언어로 구성하기로 결정 되었습니다. 신규 프로젝트에 있어서 가장 중요한 부분이 인증 및 인가 시스템인데요.여기서 우리는 JWT + Spring Security 를 사용하기로 결정되었고특히 Social Login 경우 Grant Type 을 "Authorization Code Grant Type" 을 이용하기로 했습니다.아무래도 Authorization Code Grant Type 이 가장 안전하기 때문 입니다. 앞써 말씀드렸지만 Client Server 를 vue, react, Android, ois 를 이용 계획이고Authorization Code Grant Type 을 동시에 이용하면서 인증 처리 할 예정인데요. 인증이 성공적으로 완료된다면 자체적으로 JWT 토큰을 생성해서 Access Token 을 만들고 Client 서버 에게 반환 해서 반환 받은 Client 서버들을 Resouce Server 에서 자원을 요청 하는 시스템입니다. 우선 이미지를 보면서 시나리오를 설명 드리도록 하겠습니다. 일반 로그인A 사용자가 아이디 하고 패스워드를 입력 후 Authorization Server(Spring Boot) 로 전송한다. 인증 처리가 완료 되면 A 사용자 그러니깐 Client 서버에게 자체적으로 JWT Token 을 만들어서 Access Token을 전송한다. Client Server 는 자체적으로 만든 JWT Token (Access Token) 을 쿠키에 저장 하고 Resource Server 에 요청 한다. Social 로그인Client 서버 접속한 사용자는 KaKao 소셜 로그인 버튼을 클릭한다. (요청 파라미터는 카카오로 부터 제공 받은 client_id, redirect_uri + response_type 을 'code' 로 지정한다.) KaKao Authorization Server 는 요청 받은 파라미터로 Client 식별하고 사용자에게 kakao 로그인 페이지를 보여드린다. 생략 되었지만 인가 처리되었다면 임시 코드값을 지정한 redirect uri 통해 client server 로 전송한다. 임시 코드를 받은 Client Server 는 자체적으로 구축한 Authorization Server(Spring Boot) 로 전송한다. 임시 코드를 전송 받은 데이터와 client_id, client_secret, response_type 함께 KaKao Authorization Server 로 요청한다 (Access Token 받기 위해) 인증이 완료되면 KaKao Authorization Server 는 Authorization Server(Spring Boot) 로 인가처리가 완료되었음을 의미하는 Access Token 만들어 전송한다. Access Token 을 발급 받은 Authorization Server(Spring Boot) 서버는 해당 Access Token 을 이용해서 인증 처리하기 위해 KaKao Authorization Server 로 요청한다. 인증 처리 완료되었다면 해당 접속자의 유저 정보를 전송 한다. 해당 유저 정보를 이용해서 회원 가입 프로세스 처리 동시에 자체적으로 JWT Token (Resource Server 접근 하기 위한) 을 생성해서 Client Server에 반환 한다. Client Server 는 자체적으로 만든 JWT Token (Access Token) 을 쿠키에 저장 하고 Resource Server 에 요청 한다. 선생님 강의에서는 Client Server 경우 Spring Boot Security 을 이용했던거와 달리클라이언트 서버 경우 Vue, React, Android, IOS 사용 할려고 합니다.그리고 Grant Type 을 "Authorization Code Grant Type" 사용 하기 때문에 일반 로그인 경우는 문제가 없는데Social 로그인 경우 프로세스를 어떻게 하면 잘 구성 될지 제 나름대로 고민을 해보았습니다. Implicit Grant Type 경우를 사용하면 쉽게 처리 할 수 있을것 같은데Grant Type 을 "Authorization Code Grant Type" 사용 할려다 보니 많이 복잡해지고 이렇게 작동이 될지 의문도 들고 이런 저런 생각이 드네요. 또한 Client Server 경우 절대로 client_secret 을 노출 해서는 안되다는 점 제한으로 이러저런 구성하는데 힘이 좀 들었습니다한번 선생님에게 제가 구상한 프로세스를 피드백 받고 싶어 이렇게 질문 드립니다. 항상 소중한 강의 제공해주셔서 감사드리고 답변 부탁드립니다. 감사합니다 :)
-
미해결MERN STACK 커뮤니티 : 시작부터 배포까지 알려주는 React
edit 부분에 질문이 있습니다.
edit 부분에 질문이 있습니다. 이렇게 수정버튼을 누르면 제목과 내용은 로딩이 되는데 이미지 부분만이 로딩이 안되어서 질문을 드립니다. post js var express = require("express"); var router = express.Router(); const multer = require("multer"); const { Post } = require("../Model/Post.js"); const { Counter } = require("../Model/Counter.js"); const { User } = require("../Model/User.js"); const setUpload = require("../Util/upload.js"); router.post("/submit", (req, res) => { let temp = { title: req.body.title, content: req.body.content, image: req.body.image, }; Counter.findOne({ name: "counter" }) .exec() .then((counter) => { temp.postNum = counter.postNum; User.findOne({ uid: req.body.uid }) .exec() .then((userInfo) => { temp.author = userInfo._id; const CommunityPost = new Post(temp); CommunityPost.save().then((doc) => { Counter.updateOne( { name: "counter" }, { $inc: { postNum: 1 } } ).then(() => { res.status(200).json({ success: true }); }); }); }); }) .catch((err) => { res.status(400).json({ success: false }); }); }); router.post("/list", (req, res) => { let sort = {}; if (req.body.sort === "최신순") { sort.createdAt = -1; } else { //인기순 sort.repleNum = -1; } Post.find({ $or: [ { title: { $regex: req.body.searchTerm } }, { content: { $regex: req.body.searchTerm } }, ], }) .populate("author") .sort(sort) .skip(req.body.skip) // 0, 5 .limit(5) //한번에 찾을 doc 숫자 .exec() .then((doc) => { res.status(200).json({ success: true, postList: doc }); }) .catch((err) => { res.status(400).json({ success: false }); }); }); router.post("/detail", (req, res) => { Post.findOne({ postNum: Number(req.body.postNum) }) .populate("author") .exec() .then((doc) => { res.status(200).json({ success: true, post: doc }); }) .catch((err) => { res.status(400).json({ success: false }); }); }); router.post("/edit", (req, res) => { let temp = { title: req.body.title, content: req.body.content, image: req.body.image, }; Post.updateOne({ postNum: Number(req.body.postNum) }, { $set: temp }) .exec() .then(() => { res.status(200).json({ success: true }); }) .catch((err) => { res.status(400).json({ success: false }); }); }); router.post("/delete", (req, res) => { Post.deleteOne({ postNum: Number(req.body.postNum) }) .exec() .then(() => { res.status(200).json({ success: true }); }) .catch((err) => { res.status(400).json({ success: false }); }); }); /* const storage = multer.diskStorage({ destination: function (req, file, cb) { cb(null, "image/"); }, filename: function (req, file, cb) { cb(null, `${Date.now()}_${file.originalname}`); }, }); const upload = multer({ storage: storage }).single("file"); router.post("/image/upload", (req, res) => { upload(req, res, (err) => { if (err) { res.status(400).json({ success: false }); } else { res.status(200).json({ success: true, filePath: res.req.file.path }); } }); }); */ router.post( "/image/upload", setUpload("react-community/post"), (req, res, next) => { res.status(200).json({ success: true, filePath: res.req.file.location }); } ); module.exports = router; edit js import React, { useState, useEffect } from "react"; import { useParams } from "react-router-dom"; import axios from "axios"; import ImageUpload from "./ImageUpload.js"; import { useNavigate } from "react-router-dom"; // import { UploadButtonDiv, UploadDiv, UploadForm, } from "../../Style/UploadCSS.js"; function Edit() { let navigate = useNavigate(); let params = useParams(); const [PostInfo, setPostInfo] = useState([]); const [Flag, setFlag] = useState(false); const [Title, setTitle] = useState(""); const [Content, setContent] = useState(""); const [Image, setImage] = useState(""); useEffect(() => { setTitle(PostInfo.title); setContent(PostInfo.content); setImage(PostInfo.image); console.log(PostInfo.image); }, [PostInfo]); useEffect(() => { let body = { postNum: params.postNum, }; axios .post("/api/post/detail", body) .then((response) => { if (response.data.success) { setPostInfo(response.data.post); setFlag(true); } }) .catch((err) => { console.log(err); }); }, []); const onSubmit = (e) => { e.preventDefault(); if (Title === "" || Content === "") { return alert("모든 항목을 채워 주십쇼"); } let body = { title: Title, content: Content, image: Image, postNum: params.postNum, }; axios .post("/api/post/edit", body) .then((response) => { if (response.data.success) { alert("글 수정이 완료되었습니다."); navigate(`/post/${params.postNum}`); } else { alert("글 수정에 실패하였습니다."); } }) .catch((err) => { console.log(err); }); }; return ( <UploadDiv> {Flag && ( <UploadForm> <label htmlFor="label">제목</label> <input id="title" type="text" value={Title} onChange={(e) => { setTitle(e.currentTarget.value); }} /> <ImageUpload setImage={setImage} /> <label htmlFor="content">내용</label> <textarea id="content" value={Content} onChange={(e) => { setContent(e.currentTarget.value); }} /> <UploadButtonDiv> <button className="cancel" onClick={(e) => { e.preventDefault(); navigate(-1); }} > 취소 </button> <button onClick={(e) => { onSubmit(e); }} > 제출 </button> </UploadButtonDiv> </UploadForm> )} </UploadDiv> ); } export default Edit; detail js import React, { useState, useEffect } from "react"; import { Link } from "react-router-dom"; import { useParams, useNavigate } from "react-router-dom"; import axios from "axios"; import Spinner from "react-bootstrap/Spinner"; import { PostDiv, Post, BtnDiv, SpinnerDiv, } from "../../Style/PostDetailCSS.js"; function Detail() { let navigate = useNavigate(); let params = useParams(); const [PostInfo, setPostInfo] = useState([]); const [Flag, setFlag] = useState(false); useEffect(() => { let body = { postNum: params.postNum, }; axios .post("/api/post/detail", body) .then((response) => { console.log(response); if (response.data.success) { setPostInfo(response.data.post); setFlag(true); } }) .catch((err) => { console.log(err); }); }, []); useEffect(() => { console.log(PostInfo); }, [PostInfo]); const DeleteHandler = () => { if (window.confirm("정말로 삭제를 하시겠습니까?")) { let body = { postNum: params.postNum, }; axios .post("/api/post/delete", body) .then((response) => { if (response.data.success) { alert("게시글이 삭제 되었습니다."); navigate("/"); } }) .catch((err) => { alert("게시글이 삭제에 실패하였습니다."); }); } }; return ( <PostDiv> {Flag ? ( <> <Post> <h1>{PostInfo.title}</h1> {PostInfo.image ? ( <img src={`http://localhost:5000/${PostInfo.image}`} alt="" style={{ width: "100%", height: "auto" }} /> ) : null} <p>{PostInfo.content}</p> </Post> <BtnDiv> <Link to={`/edit/${PostInfo.postNum}`}> <button className="edit">수정</button> </Link> <button className="delete" onClick={() => DeleteHandler()}> 삭제 </button> </BtnDiv> </> ) : ( <SpinnerDiv> <Spinner animation="border" role="status"> <span className="visually-hidden">Loading...</span> </Spinner> </SpinnerDiv> )} </PostDiv> ); } export default Detail; imageupload import React from "react"; import { Form } from "react-bootstrap"; import axios from "axios"; function ImageUpload(props) { const FileUpload = (e) => { var formData = new FormData(); formData.append("file", e.target.files[0]); axios.post("/api/post/image/upload", formData).then((response) => { props.setImage(response.data.filePath); }); }; return ( <div> <Form.Control type="file" className="shadow-none" accept="image/*" onChange={(e) => FileUpload(e)} /> </div> ); } export default ImageUpload; upload js import React, { useState, useEffect } from "react"; import ImageUpload from "./ImageUpload.js"; import { UploadButtonDiv, UploadDiv, UploadForm, } from "../../Style/UploadCSS.js"; import axios from "axios"; import { useNavigate } from "react-router-dom"; function Upload(props) { const [Title, setTitle] = useState(""); const [Content, setContent] = useState(""); const [Image, setImage] = useState(""); let navigate = useNavigate(); const onSubmit = (e) => { e.preventDefault(); if (Title === "" || Content === "") { return alert("모든 항목을 채워 주십쇼"); } let body = { title: Title, content: Content, image: Image, }; axios .post("api/post/submit", body) .then((response) => { console.log(response); if (response.data.success) { alert("글 작성이 완료되었습니다."); navigate("/"); } else { alert("글 작성에 실패하였습니다."); } }) .catch((err) => { console.log(err); }); }; return ( <UploadDiv> <UploadForm> <label htmlFor="label">제목</label> <input id="title" type="text" value={Title} onChange={(e) => { setTitle(e.currentTarget.value); }} /> <ImageUpload setImage={setImage} /> <label htmlFor="content">내용</label> <textarea id="content" value={Content} onChange={(e) => { setContent(e.currentTarget.value); }} /> <UploadButtonDiv> <button button onClick={(e) => { onSubmit(e); }} > 제출 </button> </UploadButtonDiv> </UploadForm> </UploadDiv> ); } export default Upload;