묻고 답해요
158만명의 커뮤니티!! 함께 토론해봐요.
인프런 TOP Writers
-
해결됨[리뉴얼] React로 NodeBird SNS 만들기
로그아웃시 req logout의 콜백함수 에러가 납니다...
req.logout을 호출할때 콜백함수가 필요하다 라고 나옵니다.req#logout requires a callback function이런식으로요 제 코드는 제로초님 코드처럼 적어놨는데 제가 이해하는 콜백함수라는게 라우터 부분이라고 생각이 드는데 어떻게 수정을 해야할지 감이 안잡힙니다 ㅜㅜ const express = require('express'); const bcrypt = require('bcrypt'); const passport = require('passport'); const { isLoggedIn, isNotLoggedIn } = require('./middlwares') const { User, Post } = require('../models'); const router = express.Router(); router.post("/login", isNotLoggedIn, (req, res, next) => { passport.authenticate("local", (err, user, info) => { if (err) { // 서버에러 부분 console.error(err) return next(err) } if(info){// 클라이언트 에러 부분 return res.status(401).send(info.reason) } // 성공시 return req.login(user,async(loginErr)=>{ // 패스포트 에러날시 if(loginErr){ console.error(loginErr) return next(loginErr) } // 유저의 모든 정보 const fullUserWithoutPassword = await User.findOne({ where:{id:user.id}, // 비밀번호 제외 // 받고 싶은 정보만 적을경우 // attributes:["id","nickname","email"], attributes: { exclude:["password"] }, // 나머지 정보 include:[{ model:Post }, { model: User, as: "Followings", }, { model: User, as: "Followers", }] }) // post 팔로워 팔로잉 정보 비밀번호 제거 return res.status(200).json(fullUserWithoutPassword) }) })(req,res,next) }) router.post("/", isNotLoggedIn, async (req, res, next) => { try { const exUser = await User.findOne({// 중복이 됫는지아닌지. where:{ email:req.body.email, } }) if(exUser){ return res.status(403).send("이미 사용중인 아이디입니다.") } const hashedPassword = await bcrypt.hash(req.body.password, 12) await User.create({ email:req.body.email, nickname: req.body.nickname, password: hashedPassword, }) // res.setHeader("Access-Control-Allow-Origin","http://localhost:3000") res.status(201).send("회원가입이 완료되셨습니다.") // 201은 생성이 잘됫다. } catch (error) { console.error(error) next(error)// 에러가 난거를 브라우저에 알려준다. statsus:500 } }) router.post('/logout', (req, res) => { req.logout(); req.session.destroy(); res.send('ok'); }); module.exports = router
-
미해결Spring Cloud로 개발하는 마이크로서비스 애플리케이션(MSA)
서버의 콘솔에 관련된 질문
강의 감사하게 잘 듣고 있습니다.궁금한게 있어서 문의드립니다.프로그램을 셋팅하고 서버를 start하면 이렇게 로그가 출력되면서 왼쪽 빌드창에 계속 뭐가 돌고 있는게 보이는데 이게 서버로 설정해놓아서 그런건가요?
-
미해결[React 2부] 고급 주제와 훅
2장에서 React Context를 구현하는 방식이 실제 Context가 동작하는 방식이라고 생각해도 무방할가요?
Context 내부 원리를 알 수 있어서 도움이 많이 되었습니다.궁금한건 Context가 이렇게 동작하구나하고 이해해도 되겠죠?" EventEmitter 패턴이 Context의 원리이다 " 라고 이해하고 넘어가고 싶은데 정환님은 이러한 정보를 어떻게 알 수 있었을까요? 리액트 공식 홈페이지에 이러한 내용이 있을까요?
-
해결됨웹 게임을 만들며 배우는 React
리액트 class 컴포넌트 중 this 질문입니다.
class LikeButton extends React.Component { constructor(props) { super(props); this.state = {liked: false}; this.onClickButton = this.onClickButton.bind(this) // 이걸 안써주면 동작 안함 } onClickButton() { this.setState({liked: true}); } render() { if (this.state.liked) { return 'You liked this.'; } return ( <button onClick={this.onClickButton}> Like </button> ); } }이 코드에서 this가 button 태그를 가르키기 때문에 bind함수로 LikeButton 클래스 인스턴스로 바인딩 해주어야 화살표 함수가 아닌 일반 function 키워드 함수로 메서드를 정의했을때 동작하는게 맞는걸까요? 화살표 함수라면 button 태그를 가르키지 않고 바깥 this를 그대로 가져오기 때문에 LikeButton 클래스 인스턴스를 가져오는 것이 맞을까요?
-
미해결스프링 시큐리티 OAuth2
spring boot 3.1 security entryPoint 관련 질문입니다
스프링부트 2.7.7 버전에서는 사용할때 아무 문제없던 코드들인데 3.1 버전 업데이트하면서 securityFilterChain 이부분 문법만 살짝 바뀐정도입니다 그런데jwt 토큰에서 chain.doFilter(request, response); 까지 잘 넘어가고비즈니스 로직인 service 단에서 에러가 터졌을때 (NullPointException,IllegalArgumentException)에러가 났을경우다시 엔트리포인트로 넘어와서 에러처리가 되는데 왜 이럴까요 ?? 무슨 에러가 나든엔트리포인트로 넘어오는데 엔트리포인트는 인증 실패일경우에 실행되어야하는걸로 알고있습니다토큰값은 모두 유효하고 jwt 토큰 인증은 되었음에도 불구하고 엔트리포인트로 넘어가는 이유를 모르겠습니다아래 에러는 비즈니스로직에서 에러 난 코드이며 2023-10-30T23:56:16.287+09:00 ERROR 26006 --- [nio-8081-exec-6] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed: org.springframework.dao.InvalidDataAccessApiUsageException: No enum constant com.tripcoach.core.domain.alarm.enums.AlarmType.COACH] with root causejava.lang.IllegalArgumentException: No enum constant com.tripcoach.core.domain.alarm.enums.AlarmType.COACHat java.base/java.lang.Enum.valueOf(Enum.java:273) ~[na:na]at org.hibernate.type.descriptor.java.EnumJavaType.fromName(EnumJavaType.java:231) ~[hibernate-core-6.2.9.Final.jar:6.2.9.Final]at org.hibernate.type.descriptor.converter.internal.NamedEnumValueConverter.toDomainValue(NamedEnumValueConverter.java:53) ~[hibernate-core-6.2.9.Final.jar:6.2.9.Final] 바로 에러코드 다음이 2023-10-30T23:56:16.291+09:00 ERROR 26006 --- [nio-8081-exec-6] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] threw exceptionjava.lang.NullPointerException: Cannot invoke "Object.getClass()" because "exception" is nullat org.springframework.web.method.annotation.ExceptionHandlerMethodResolver.resolveMethodByThrowable(ExceptionHandlerMethodResolver.java:146) ~[spring-web-6.0.12.jar:6.0.12]at org.springframework.web.method.annotation.ExceptionHandlerMethodResolver.resolveMethod(ExceptionHandlerMethodResolver.java:134) ~[spring-web-6.0.12.jar:6.0.12] 엔트리포인트에서 넘어와버리네요부족한 코드이지만 아무리 찾아도 모르겠어서 글 남깁니다. 아래 전체코드 첨부합니다
-
미해결3시간에 끝내는 디지털 마케팅의 모든 것
구좌계약의 단점 질문
강사님 안녕하세요! 강의 수강중에 질문사항이 있어 글 올립니다.콘텐츠 마케팅의 구좌계약의 단점에서 중간단계 확인불가로 인해 성과추적한계가 있다고 말씀하셨는데 여기서 중간단계란 어떤 의미인지 또 왜 확인이 불가한지 궁금합니다! 답변해주시면 감사하겠습니다!
-
미해결스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술
Constructor와 Getter Setter
[질문 내용]24분 11초 쯤 내용에서 변수 표현식에서 프로퍼티 접근법을 사용한다고 하셨는데, 따로 item.getPrice() 함수가 없는데...Constructor 라는 생성자가 알아서 getter/setter 역할을 해주는건지 여쭤봅니다... 분명 강의를 들었을 텐데...기억이 나지 않아 여쭤봅니다.. this.price = price 가 getPrice 랑 같은건지... getter 방식과 같은건지...요.
-
해결됨[퇴근후딴짓] 빅데이터 분석기사 실기 (작업형1,2,3)
궁금해서 문의남깁니다.
챕터 5-2 관련 질문입니다.강의 따라서 그대로 수행했고중간에 RandomRegressor의 경우 강사님께서 훈련을 잘못 적용하신 것 까진 이해를 했습니다.다만 궁금한건 xgboost로 훈련을 한 경우검증데이터로 r2 score를 도출했을 경우엔 0.27로 다른 회귀분석을 하는 경우보다 높게 나왔습니다만,최종적으로 평가하는 y_test와 x_test 모형에서는 결과값이...-0.03400982959617549 라는 비정상적인 값이 나옵니다...올려진 sheets 중 y_test값이 잘못 된것인지...값이 이상하여 문의 댓글 남깁니다. 제가 사용한 코드는 proba = xg.predict(test) y_test = pd.read_csv("y_test.csv") print(r2_score(y_test, proba))였고 하필 강의 말미에도 총 평가점수가 나오진 않아있어 문으드립니다. 답변 기다리겠습니다.
-
해결됨입문자를 위한 자바스크립트 기초 강의
배열 메소드 2편 질문 드립니다.
const tw = ['나연','사나','지효','다현']; tw.forEach(function( member, index ){ const p = document.createElement('p') p.textContent = `${index+1} 번째 멤버는 ${member}` document.body.appendChild(p); }) 위처럼 작성을 했는데요. 아래처럼 에러가 발생합니다. 제가 어디가 틀렸는지 감이 안 잡혀서요 ㅠUncaught TypeError: Cannot read properties of null (reading 'appendChild') // 크롬 버전 : 버전 118.0.5993.118(공식 빌드) (64비트)
-
미해결Vue 3 & Firebase 10 커뮤니티 만들기 풀스택 - "활용편" (with Pinia, Quasar, Tiptap, VueUse)
14강 에러문의 드립니다!
<template v-for="(_, name) in $slots" :key="name"> 여기 부분에서 _ 이 부분에 배열 요소 구조 파괴 패턴이 필요합니다.ts(1181), 문제 보기 (<Alt>+F8) 빠른 수정을 사용할 수 없음 이 에러가 나거든요??근데 또 실행은 잘 되는데, 빨간색 줄이 여간 신경이 쓰여서...ㅎㅎㅎ; 문의드립니다.
-
미해결[리뉴얼] React로 NodeBird SNS 만들기
post 추가 시 오류
안녕하세요 제로초님다름이 아니라 로그인 후 포스트를 추가할 때 에러가 발생하는데혼자 해결하려고 노력해봤지만 해결을 아직 하지 못하여 질문드립니다.reducers/post.jsimport shortId from "shortid"; import { produce } from "immer"; import faker from "faker"; export const initialValue = { mainPosts: [], //이미지업로드 할떄 이미지경로들이 여기 들어간다. imagePaths: [], hasMorePost: true, //true면 가져올 시도를 해라. (스크롤 했을 때) loadPostsLoading: false, loadPostsDone: false, loadPostsError: null, //게시글 추가가 완료되었을때 TRue addPostLoading: false, addPostDone: false, addPostError: null, removePostLoading: false, removePostDone: false, removePostError: null, addCommentLoading: false, addCommentDone: false, addCommentError: null, }; //가짜 데이터 export const generateDummyPost = (number) => Array(number) .fill() .map(() => ({ id: shortId.generate(), User: { id: shortId.generate(), nickname: faker.name.findName(), }, content: faker.lorem.paragraph(), //아무 문장, Images: [ { src: "https://image.utoimage.com/preview/cp872722/2022/12/202212008462_500.jpg", }, ], Comments: [ { User: { id: shortId.generate(), nickname: faker.name.findName(), }, content: faker.lorem.sentence(), }, ], })); export const LOAD_POSTS_REQUEST = "LOAD_POSTS_REQUEST"; export const LOAD_POSTS_SUCCESS = "LOAD_POSTS_SUCCESS"; export const LOAD_POSTS_FAILURE = "LOAD_POSTS_FAILURE"; export const ADD_POST_REQUEST = "ADD_POST_REQUEST"; export const ADD_POST_SUCCESS = "ADD_POST_SUCCESS"; export const ADD_POST_FAILURE = "ADD_POST_FAILURE"; export const REMOVE_POST_REQUEST = "REMOVE_POST_REQUEST"; export const REMOVE_POST_SUCCESS = "REMOVE_POST_SUCCESS"; export const REMOVE_POST_FAILURE = "REMOVE_POST_FAILURE"; export const ADD_COMMENT_REQUEST = "ADD_COMMENT_REQUEST"; export const ADD_COMMENT_SUCCESS = "ADD_COMMENT_SUCCESS"; export const ADD_COMMENT_FAILURE = "ADD_COMMENT_FAILURE"; export const addPost = (data) => ({ type: ADD_POST_REQUEST, data, }); export const addComment = (data) => ({ type: ADD_COMMENT_REQUEST, data, }); //리듀서란 이전 상태를 액션을 통해 다음 상태로 만들어내는 함수(단, 불변성은 지키면서) //draft는 불변성 상관없이 바꾸면 immer가 알아서 불변성있게 만들어준다. const reducer = (state = initialValue, action) => produce(state, (draft) => { switch (action.type) { //게시글 추가 case ADD_POST_REQUEST: draft.addPostLoading = true; draft.addPostDone = false; draft.addPostError = null; break; case ADD_POST_SUCCESS: draft.addPostLoading = false; draft.addPostDone = true; draft.mainPosts.unshift(action.data); draft.imagePaths = []; //unshift란 배열의 맨 앞에다가 추가하는 함수 break; case ADD_POST_FAILURE: draft.addPostLoading = false; draft.addPostError = action.error; break; //게시글 불러오기 case LOAD_POSTS_REQUEST: draft.loadPostsLoading = true; draft.loadPostsDone = false; draft.loadPostsError = null; break; case LOAD_POSTS_SUCCESS: draft.loadPostsLoading = false; draft.loadPostsDone = true; draft.mainPosts = action.data.concat(draft.mainPosts); //concat은 두개 이상의 배열을 합칠 때 사용 //action.data에는 더미데이터들이 들어있고 draft.mainPosts는 원래 데이터 draft.hasMorePost = draft.mainPosts.length < 50; //50개보다 적으면 불러와야함 break; case LOAD_POSTS_FAILURE: draft.loadPostsLoading = false; draft.loadPostsError = action.error; break; //댓글 추가 case ADD_COMMENT_REQUEST: draft.addCommentLoading = true; draft.addCommentDone = false; draft.addCommentError = null; break; case ADD_COMMENT_SUCCESS: //immer버전 (너무 간단함) const post = draft.mainPosts.find( (v) => v.id === action.data.postId ); //해당 게시글 찾기 post.Comments.unshift(dummyComment(action.data.content)); draft.addCommentLoading = false; draft.addCommentDone = true; //댓글 넣어주기 break; //immer를 안 쓴 부분 // const postIndex = state.mainPosts.findIndex( // (v) => v.id === action.data.postId // ); // const post = { ...state.mainPosts[postIndex] }; // post.Comments = [ // dummyComment(action.data.content), // ...post.Comments, // ]; //얕은 복사 // const mainPosts = [...state.mainPosts]; // mainPosts[postIndex] = post; //댓글 추가하는 부분 너무어려움.. // //불변성을 지키다 보니 가독성이 너무 안좋음 // return { // ...state, // mainPosts, // addCommentLoading: false, // addCommentDone: true, // }; case ADD_COMMENT_FAILURE: draft.addCommentLoading = false; draft.addCommentError = action.error; break; //게시글 삭제 case REMOVE_POST_REQUEST: draft.removePostDone = false; draft.removePostLoading = true; draft.removePostError = null; break; case REMOVE_POST_SUCCESS: draft.removePostLoading = false; draft.removePostDone = true; draft.mainPosts = draft.mainPosts.filter( (v) => v.id !== action.data ); break; case REMOVE_POST_FAILURE: draft.removePostLoading = false; draft.removePostError = action.error; break; default: break; } }); export default reducer;여기서 Images안에 아무것도 들어있지 않아서 나타나는 오류같은데 잘 모르겠습니다.
-
해결됨[코드캠프] 부트캠프에서 만든 고농축 백엔드 코스
ElasticSearch 개념 내용은 어디서 볼 수 있나요?
ElasticSearch 개념을 학습할 수 있는 부분이 검색 섹션에 있을 거 같은데 보이지 않아 여쭤봅니다.
-
해결됨나도코딩의 자바 기본편 - 풀코스 (20시간)
퀴즈 2번 질문드립니다
코드를 아래와 같이 작성했더니int height = 115; String result = (height >= 120) ? "탑승 가능합니다" : "탑승 불가능합니다"; System.out.println("키가 " + height + "cm 이므로 " + result); height = 121; System.out.println("키가 " + height + "cm 이므로 " + result);키가 115cm 이므로 탑승 불가능합니다키가 121cm 이므로 탑승 불가능합니다 이렇게 출력되는데 중간에 변수값을 height = 121; 이런식으로 변경해주면 안되는건가요?? 121로 숫자가 바뀌는걸 봐서는 변경이 된 것 같은데 뒤에 result가 탑승 불가능합니다로 출력이 되는건지 궁금합니다!
-
미해결Vue3 완벽 마스터: 기초부터 실전까지 - "실전편"
axios에 값을 보낼때 { ...data } 하는 이유
axios의 post, put에 data를 보낼 때{ ...data }로 보내는 이유를 알려주실 수 있을까요?v-model의 값을 바로 보내면 어떤 불이익이 있을 수 있나요?
-
미해결애플 웹사이트 인터랙션 클론!
특정 타이밍 스크롤 애니메이션 적용하기 섹션 수강중입니다.
초반부 messageA_opacity_in 을 콘솔로 찍었는데요,partScrollStart보다 currentYOffset이 작을때는rv에 values[0] 즉 '0'을 리턴해야하는데,그러지 않고 -1부터 0까지 쭉 커지다가 css가 적용되는 구간부터 1로 점점 커집니다...const sceneInfo = [ { //0 type:'sticky', heightNum:5,//브라우저 높이의 배수 세팅 scrollHeight:0, //각 씬의 스크롤 높이 objs:{ container: document.querySelector('#scroll-section-0'), messageA: document.querySelector('#scroll-section-0 .main-message.a'), messageB: document.querySelector('#scroll-section-0 .main-message.b'), messageC: document.querySelector('#scroll-section-0 .main-message.c'), messageD: document.querySelector('#scroll-section-0 .main-message.d') }, values:{ messageA_opacity: [0, 1, { start: 0.1, end: 0.2}], messageB_opacity: [0, 1, { start: 0.3, end: 0.4}] } }, { //1 type:'normal', heightNum:5,//브라우저 높이의 배수 세팅 scrollHeight:0, //각 씬의 스크롤 높이 objs:{ container: document.querySelector('#scroll-section-1') } }, { //2 type:'sticky', heightNum:5,//브라우저 높이의 배수 세팅 scrollHeight:0, //각 씬의 스크롤 높이 objs:{ container: document.querySelector('#scroll-section-2') } }, { //3 type:'sticky', heightNum:5,//브라우저 높이의 배수 세팅 scrollHeight:0, //각 씬의 스크롤 높이 objs:{ container: document.querySelector('#scroll-section-3') } } ];function calcValues(values, currentYOffset){ let rv; const scrollHeight = sceneInfo[currentScene].scrollHeight const scrollRatio = currentYOffset / scrollHeight if(values.length === 3){ // start ~ end 사이의 애니메이션 실행 const partScrollStart = values[2].start * scrollHeight; const partScrollEnd = values[2].end * scrollHeight; const partScrollHeight = partScrollEnd - partScrollStart; if(currentYOffset => partScrollStart && currentYOffset <= partScrollEnd){ rv = (currentYOffset - partScrollStart) / partScrollHeight * (values[1] - values[0]) + values[0]; } else if (currentYOffset < partScrollStart){ rv = values[0]; } else if (currentYOffset > partScrollEnd){ rv = values[1]; } } else { rv = scrollRatio * (values[1] - values[0]) + values[0]; } return rv; } function playAnimation(){ const objs = sceneInfo[currentScene].objs; const values = sceneInfo[currentScene].values; const currentYOffset = yOffset - prevScrollHeight; switch (currentScene){ case 0: let messageA_opacity_in = calcValues(values.messageA_opacity, currentYOffset) objs.messageA.style.opacity = messageA_opacity_in console.log(messageA_opacity_in) break; case 1: break; case 2: break; case 3: break; } } 도와주세요 ㅠ
-
미해결[LV1] Jetpack Compose - UI 연습하기
Retrofit 관련 url 주소를 적을 때 주의사항입니다
// HTTP 로 요청이 필요한 부분을 interface 로 정의interface MyApi { @GET("/posts/1")// GET 요청임을 알리고, 서버내의 경로(path)를 지정 // BASE_URL 이 https://jsonplaceholder.typicode.com 일 경우 @GET("/posts/1") 로 적고// BASE_URL 이 https://jsonplaceholder.typicode.com/ 일 경우 @GET("posts/1") 로 적는다suspend fun getPost1() : Response<Post> } 위와 같이 정리해 보았는데 이게 맞는지 ㅎㅎㅎ
-
미해결일잘하는 마케터, MD에게 꼭 필요한 파이썬 데이터 분석
6강 WebDriverException
안녕하세요. 아래에 같은 질문을 하신 분이 있던데, 똑같이 소스 코드를 붙여도 작동이 안 돼서 문의 남깁니다. 작성한 코드 링크로 첨부해 드립니다.https://colab.research.google.com/drive/1JA-kiri-MhZHNSQ6jGDxE2Mv7JQHFzuP?hl=ko#scrollTo=9Zme4ZGxP1qR어떤 게 문제일까요 🥲🥲 미리 답변 감사합니다... 귀한 강의도 감사합니다...
-
해결됨디자인 시스템 with 피그마
타이포그라피 관련 질문
안녕하세요 선생님 타이포 그라피 단락 부분 듣다가요!궁금증이 생겼습니다.네임부분은 첫글자를 소문자로 사용하시고 폰트 웨이트 부분은 대문자로 시작하셨는데요 입력할때 대소문자 규칙 같은것이 있는 걸까요? (상관없나요 보통?)나중에 실무에서는 저런 네이밍 규칙은 보통 어떻게 진행되는지 궁금하여 질문 드립니다.! :)
-
해결됨10주완성 C++ 코딩테스트 | 알고리즘 코딩테스트
1-K 맞왜틀?
http://boj.kr/1cea1d70b28a4da8b871d48acb7c4907 홀수가 2개 이상이면 팬린드롬이 불가하고 아니면 팰린드롬을 만드는데알파벳 개수가 1개이면 홀수 문자에 넣고 아니면 개수의 반 만큼 word에 넣는다 이때, 홀수개이면 홀수 문자에 넣고 끝에 추가한다 그리고 뒤집은 word를 추가한다 이런 식으로 짰는데 실행하면 맞는데 왜 틀렸다고 뜨는지 궁금합니다!
-
해결됨스프링 부트 - 핵심 원리와 활용
javaw 로 백그라운드 실행한 이후에 재시작할 때는 종료 후 시작으로 하면 되나요?
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.1. 강의 내용과 관련된 질문을 남겨주세요.2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요.(자주 하는 질문 링크: https://bit.ly/3fX6ygx)3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요.(질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG)질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요.=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예/아니오)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오)[질문 내용]저희 회사는 지금 외장 톰캣을 사용하고 있습니다. 그런데 외장 톰캣과 관련해서 SpringApplicationShutdownHook 에 버그가 있고, 최근까지도 spring github 에 issue 로 보고된 것으로 알고 있습니다. 그 외에도 불편한 건 좀 있지만 특히 이 버그 때문에 내장 톰캣으로 배포해야겠다는 생각을 했습니다. java -jar 로 실행하면 포어그라운드에서 실행되며 프롬프트를 나가면 해당 프로세스도 자동으로 종료가 됩니다. 그래서 javaw -jar 로 실행해서 백그라운드로 실행하려고 합니다. 1. 한 번 배포한 이후에는 매번 백그라운드의 프로세스를 종료하고 다시 시작하면 되나요?2. 내장 톰캣 방식으로 애플리케이션을 실행하면, 외장 톰캣에 ROOT.war 를 옮기면 자동으로 재시작하는 그런 방법은 더 이상 사용할 수 없나요?3. 1번과 2번이 그렇다는 가정 하에 제가 아는 지식 선에서는, 서비스의 중단 없이 배포를 하기 위해서는 2대의 서버 또는 서비스와 로드 밸런서를 활용해서 순차적으로 2개의 애플리케이션에 재시작 스크립트를 재실행하면 될 것 같습니다(CentOS 와 젠킨스를 사용하고 있습니다).혹시 실무에서 더 추천할 만한 배포 방식이나, 관련 레퍼런스를 알기 위한 키워드, 공부 방향을 배울 수 있을까요??