묻고 답해요
167만명의 커뮤니티!! 함께 토론해봐요.
인프런 TOP Writers
-
해결됨RabbitMQ를 이용한 비동기 아키텍처 한방에 해결하기
Pub/Sub & WebSocket 활용한 실시간 알림
SSE & Redis와 Pub/Sub & WebSocket을 비교하고 있는데요.실시간성과 정확성을 위해서라면 Pub/Sub & WebSocket을 더 많이 쓰나요? 알림의 특성상 채팅과 같은 양방향은 아니기 때문에 WebSocket이 적합한 선택인지 궁금합니다.
-
해결됨Spring Boot를 활용하여 채팅 플랫폼 만들어보기
checkTokenForRefresh에 대해 궁금한점
안녕하세요! 항상 강의 잘 듣고 있습니다!그런데 강의를 듣다가 checkTokenForRefresh을 해주는 메서드를 만드셨는데 제가 이해한것이 맞는지 궁금해서 질문드립니다!이 메서드에서 만료가 되면 TokenExpiredException이 일어나면 토큰이 만료되서 decode 을 하시는데 위에 메서드들 처럼 createToken을 안하는 이유가 만료된 토큰의 정보 바탕으로 바로 똑같이 바로 만들어지는 건가요?
-
미해결웹소켓/STOMP 채팅서비스(spring, vue, redis)
로컬 스토리지에 토큰 저장하는 방식 질문입니다!
보통 토큰을 로컬 스토리지에 저장하나요?그러면 명시적으로 로그아웃을 누르지 않는이상은클라이언트가 로컬스토리지 비우는 방식을 사용해서 토큰을 초기화 하지는 않을 것 같은데혹시 강의에서 로컬 스토리지 말고 쿠키 같은 것에 저장하는 내용이 뒤에서 나오나요?안나온다면 어떤식으로 처리해야하나요?서버에서 토큰을 내려보내줄때 쿠키에 담아서 내려보내주어야 하는건가요 ?
-
미해결웹소켓/STOMP 채팅서비스(spring, vue, redis)
강사님 질문있습니다~
http,websocket의 비교라는 챕터에서는(12:07)처음에 http 요청으로 연결을 맺는다라고 하셨는데순수웹소켓-백엔드 강의에서는 (6:20)http 요청이 아니기 때문에 컨트롤러에서 안받고 핸들러에서 받는다고 하셨는데중요한 개념인 것 같아서 어떻게 이해하면 될까요?검색해보니까 처음에 연결은 http 요청에 get이 맞고 헤더에 웹소켓 요청이라는 정보가 있으므로 http 요청과는 구분된다라는 정보가 있는데 잘 모르겠어요~
-
미해결웹소켓/STOMP 채팅서비스(spring, vue, redis)
강사님 프로젝트 디렉토리 구조에 대해서 질문있어요!
보통 contoller service repository 이렇게 폴더 두고 그 안에서 여러개의 MemerConrollerChatController이런식으로 나누는 것만 봐서chat과 member 디렉토리를 나누고그 안에서 다시 controller 디렉토리하고 만드는방법을 처음 써보는데요~저는 강사님 방법이 더 좋은데 보통 어떻게 사용하나요? 특별히 강사님 처럼 디렉토리 나눈 이유가 있을까요 ??
-
해결됨웹소켓/STOMP 채팅서비스(spring, vue, redis)
싱글톤 객체 질문있습니다.
@Bean public PasswordEncoder makePassword() { return PasswordEncoderFactories.createDelegatingPasswordEncoder(); }이거를 싱글톤 객체로 만들어주셨는데요그런데String encodedPassword = passwordEncoder.encode(memberSaveReqDto.getPassword()); 이부분을 static 으로 두고 참조변수를 사용해서 사용하는 것과 어떤 차이가 있나요?찾아보니 전역변수는 static 이니까 메모리에 미리 올라가고 싱글톤은 사용할떄 올라가는 장점이 있다고 하는데 이 예시는 어차피 클래스 초기화될 때 생성되니까 그런 장점이 없을 것 같은데...궁금합니다.
-
미해결웹소켓/STOMP 채팅서비스(spring, vue, redis)
메시지 저장순서에 대한 질문입니다.
안녕하세요 수업을 바탕으로 프로젝트 적용중입니다.프론트에서 채팅입력하면 백엔드의 연결된 소켓에서 이를 redis 로 publish 하고 이때 같이 채팅메시지를 db에 저장하는 방식으로 구현했습니다.나중에 채팅방에 다시 입장하는 경우 채팅 저장 날짜를 기반으로 일부 가져오려하니 초까지 같은 경우 순서가 엉키는 현상이 발생하는데 auto_increment 로 저장한 pk 를 순서로 가져와도 될까요?redis 는 싱글스레드이지만 websocket 가 병렬처리되면 결국 pk 순서가 실제 순서를 보장할 수 있을까요?
-
해결됨실전! FastAPI 활용(비동기)
비동기 서버 구성하다 알 수 없는 에러에 빠졌는데...
안녕하세요. 좋은 강의 감사합니다.강의를 기반으로 제 방식대로 서버를 구성하다가 알 수 없는 에러에 빠졌습니다.서버는 정상 구동은 되고, postman으로 root url인 localhost:8000/ 에 request를 날리면 정상적으로 결과값을 반환 받는데localhost:8000/api/recommend?userId=1 만 호출하면 바로 에러메세지 없이 500만 응답으로 받고 있습니다.print(1) 도 서버 로그에 찍히지 않고 서버 로그는 아예 나오질 않네요.localhost:8000/ 에서도 동일하게 서버 로그는 찍히지 않습니다우선 의도는 BaseRepository 클래스를 만들어서 find_by_id 같은 중복 코드를 하나로 관리해보려고 했습니다.의존성 주입 부분은 지피티의 도움을 받아서 위치를 조정했습니다.도저히 어디서 문제가 난건지 알 수 없어서 도움 요청 드립니다 ㅜㅜ# main.py from typing import Dict from dotenv import load_dotenv from fastapi import FastAPI from src.app.app import create_app load_dotenv() app: FastAPI = create_app() @app.get("/") async def health_check_handler() -> Dict[str, str]: return {"statusMsg": "good"} # app.py from contextlib import asynccontextmanager from typing import AsyncGenerator import anyio from fastapi import FastAPI from src.app.endpoints.recommend import router @asynccontextmanager async def lifespan(app: FastAPI) -> AsyncGenerator: limiter = anyio.to_thread.current_default_thread_limiter() limiter.total_tokens = 200 yield def create_app() -> FastAPI: app = FastAPI(lifespan=lifespan) app.include_router(router, prefix="/api") # 다른 설정들(예: 미들웨어, 이벤트 핸들러 등)을 추가할 수 있습니다. return app# connection.py import os import urllib from typing import AsyncGenerator from dotenv import load_dotenv from sqlalchemy.ext.asyncio import AsyncEngine, AsyncSession, async_sessionmaker, create_async_engine load_dotenv() DB_USERNAME: str = os.getenv("DB_USERNAME", "root") DB_PASSWORD: str = os.getenv("DB_PASSWORD", "root") DB_HOST: str = os.getenv("DB_HOST", "127.0.0.1") DB_NAME: str = os.getenv("DB_NAME", "") DB_PORT: str = os.getenv("DB_PORT", "3306") DB_ECHO: bool = os.getenv("DB_ECHO", "true").lower() == "true" if not DB_NAME: raise ValueError("DB_NAME 환경변수가 설정되지 않았습니다.") # 비밀번호 특수문자 허용 encoded_password = urllib.parse.quote_plus(DB_PASSWORD) DATABASE_URL: str = f"mysql+asyncmy://{DB_USERNAME}:{encoded_password}@{DB_HOST}:{DB_PORT}/{DB_NAME}" engine: AsyncEngine = create_async_engine( DATABASE_URL, echo=DB_ECHO, pool_size=10, max_overflow=0, pool_timeout=30, # second pool_recycle=60, # second pool_pre_ping=True, ) SessionFactory = async_sessionmaker(autocommit=False, autoflush=False, bind=engine) async def get_db() -> AsyncGenerator[AsyncSession, None]: session = SessionFactory() try: yield session finally: await session.close()# user_route.py from typing import Dict from fastapi import APIRouter, Depends, status from src.app.dependency.query_param_denpendency import snake_case_query from src.core.common_type import V from src.core.exception.not_found_exceptions import UserNotFoundExceiption from src.db.connection import get_db from src.entity.user import UserEntity from src.repository.user import UserRepository, get_user_repository from src.dto.response.user_response import UserResponse router = APIRouter(prefix="/recommend") @router.get(path="", status_code=status.HTTP_200_OK, response_model=UserResponse) async def get_recommend_schedule( params: Dict[str, V]=Depends(snake_case_query), user_repo: UserRepository=Depends(get_user_repository) ): print(1) user_id: int = int(params.get("user_id", None)) user: UserEntity | None = await user_repo.get_user_by_id(user_id) if not user: raise UserNotFoundExceiption() user = UserResponse.model_validate(user) return user# base_repository.py from typing import Type from sqlalchemy import select from sqlalchemy.ext.asyncio import AsyncSession from src.core.common_type import E class BaseRepository: def __init__(self, db: AsyncSession): self.db = db async def get_entity_by_id(self, model: Type[E], entity_id: int) -> E | None: print(2) entity: E | None = await self.db.execute(select(model).where(model.id==entity_id)) return entity.scalars().first() # user_repo.py from fastapi import Depends from sqlalchemy.ext.asyncio import AsyncSession from src.db.connection import get_db from src.entity.user import UserEntity from src.repository.base_repository import BaseRepository def get_user_repository(db: AsyncSession = Depends(get_db)) -> "UserRepository": return UserRepository(db) class UserRepository(BaseRepository): async def get_user_by_id(self, user_id: int) -> UserEntity | None: print(3) return await self.get_entity_by_id(UserEntity, user_id) # user_response.py from datetime import datetime from typing import Optional from pydantic import BaseModel class UserResponse(BaseModel): id: int name: str nickname: str email: str phone: str join_date: datetime updated_at: Optional[datetime] = None deleted_at: Optional[datetime] = None class Config: from_attributes = True 도대체 어디서 문제가 생긴걸까요...
-
미해결웹소켓/STOMP 채팅서비스(spring, vue, redis)
채팅 메시지 저장에 관한 질문입니다.
채팅 메시지를 보낼 때마다 db에 접근해서 저장하는 방식이면 디스크 io 작업이 굉장히 잦아질 거 같은데보통 이런 경우에는 redis 를 쓴다면 redis에 저장해두었다가 스케줄링같은 방식으로 한번에 db에 저장하는 방식을 사용하는 걸까요?
-
미해결웹소켓/STOMP 채팅서비스(spring, vue, redis)
stomp관련 질문입니다.
현재 메세지만 redis에 저장해서 공통적으로 사용되는 것으로 생각하는데 사용자의 연결정보는 각각의 서버가 가지고 있고 redis에서 메세지가 발행되는 경우 각각의 서버에서 연결된 사용자에게 메세지를 전달한다고 이해하면 될까요?
-
해결됨RabbitMQ를 이용한 비동기 아키텍처 한방에 해결하기
채팅 방식 질문입니다!
학습중 궁금한 것은 언제든 문의 하세요.질문을 최대한 자세히 남겨주시면 반드시 답변 드리도록 하겠습니다.추가로 알고 싶은 내용도 요청해주시면 강의 자료를 업데이트 해서 제공할 예정입니다. 해당 강의에서 http요청은 mq를 사용하게 되는데 채팅의 경우 스프링의 기본 내장브로커를 사용하게 되는 것이 아닌가요?
-
미해결웹소켓/STOMP 채팅서비스(spring, vue, redis)
CORS 설정 어떻게 하셨나요?
원래는 백엔드에서 진행 하는걸로 알고 있는데,간단하게 하기 위해서 프론트쪽에서 설정을 몇 번이고 진행 했는데도 불구하고 계속해서 cors 에러가 납니다.강의에서는 따로 cors 설정을 하셨던건안 나온거 같은데, cors 설정 어떻게 하셨는지 질문 드립니다.(vue.config.js)const { defineConfig } = require('@vue/cli-service'); module.exports = defineConfig({ transpileDependencies: true, devServer: { proxy: { member: { target: 'http://localhost:8080', changeOrigin: true, pathRewrite: { '^/member': '', }, }, }, }, });
-
미해결웹소켓/STOMP 채팅서비스(spring, vue, redis)
강의 내 1 번 방법에 대한 질문
강의 설명중 1 번 방법에서누가 서버에 커넥션을 맺고 있고 subscribe 하는지 서버에 메모리를 만들어서 관리하는 로직을 사용한다고 하셨는데 이전에 강의에서 배운내용에 따르면 스프링과 stomp 는 기본적으로 세션관리를 내부적으로 처리한다고 하셔서즉 저희가 직접 접근을 하지못하는데 이러면 이벤트 리스너로 따로 sessions라는 자료구조를 만든 것을 활용해서 코드를 작성해야 하잖아요 ? 그리고 세션은 랜덤으로 스프링에서 생성하는데 이러면 세션이 몇개인지 카운트 및 각각 세션id가 부여되긴하는데그게 어떤 사용자인지 구분이 안되기에 StompHandler에서 connect 맺을때 검증 후에 추가적으로 이메일정보를 String email = claims.getSubject();accessor.getSessionAttributes().put("email", email);로 설정해서 이벤트 리스너에 넘겨서 세션을 저장하는 식으로 생각했는데 맞을까요 ?이후 서버에서 처리할때 subscribe하면서 , 동시에 누가 서버에 커넥션을 맺고있는지 아닌지 여부를 파악하는 (다소 복잡한) 로직을 작성해서 처리할 수 있다 이게 맞는 흐름일까요 ?
-
해결됨웹소켓/STOMP 채팅서비스(spring, vue, redis)
npm 한번에 추가하기
npm install vue-router && npm install axios && npm install vuetify@3 && npm install jwt-decode && npm install sockjs-client && npm install webstomp-client질문은 아니고 한번에 추가할수 있는 명령어 공유하면 좋을거 같아서 작성합니다!
-
미해결웹소켓/STOMP 채팅서비스(spring, vue, redis)
jwt토큰 & 시큐리티 질문이요
안녕하세요.토큰 발행, 검증 및 시큐리티 코드가 좀 복잡 하다고 느끼는데요.이거는 한번만 개발 해놓으면 다른 곳에서도 그대로 혹은 유사하게 가져다 쓸 수 있는건가요?시큐리티 코드는 반복 되는 패턴 혹은 개념인가요?
-
미해결웹소켓/STOMP 채팅서비스(spring, vue, redis)
return type ?에 대해서
@PostMapping("/doLogin") public ResponseEntity<?> doLogin(@RequestBody MemberLoginRequestDto memberLoginRequestDto) { //email, password 검증 Member member = memberService.login(memberLoginRequestDto); //일치할 경우 access token 발행 String jwtToken = jwtTokenProvider.createToken(member.getEmail(), member.getRole().toString()); Map<String, Object> loginInfo = new HashMap<>(); loginInfo.put("id", member.getId()); loginInfo.put("token", jwtToken); return new ResponseEntity<>(loginInfo, HttpStatus.OK); } 어찌보면 자바 질문일수도 있는데요. 수업 내용 듣다가 궁금해서요! 여기 메서드 보시면 return type이 ? 사용하였는데요. ?는 어떤 타입이 와도 통과 되는 거라고 알고 있는데요. 명시적으로 특정타입으로 표현을 해도 될거 같은데 그냥 교육상 ?로 하신건가요? 실제 실무에서도 ?를 자주 사용할까요?? 코딩하다가 궁금해서 여쭤봅니다 ㅋㅋ
-
미해결웹소켓/STOMP 채팅서비스(spring, vue, redis)
jwt 인증 질문입니다.
현재 connect 할때 토큰이 올바른지 확인하는데이후 메세지를 저장할때 보낸 유저를 판단할때 이메일 정보를 이용해 보낸사람의 이메일로 유저를 찾는데 이경우 잘못될 가능성이 높을것 같아 처음 connection을 할때 저장해 두고 이걸 이용해서 보낸 유저를 구분하고 싶은데 어떻게 하는게 좋을까요?
-
미해결웹소켓/STOMP 채팅서비스(spring, vue, redis)
redis-cli 명령어로 pubsub channels 확인하는 방법
redis-cli 명령어로 pubsub channels 확인하는 방법강사님 처럼 하고나서 3000 포트와 3001 포트에서 서로 채팅하는 것 까지 테스트 해봤습니다.근데 여기서 pubsub channels 명령어로 저는 chat 이라는 값이 나올줄 아랑ㅆ는데 empty array라는 값이 나옵니다. 원래 이런가요?
-
미해결웹소켓/STOMP 채팅서비스(spring, vue, redis)
아키텍처 질문입니다
이 아키텍처 기준으로 질문이 있습니다.client1번 -> LB -> 서버 -> 레디스 -> 서버 -> client2이런 식으로 메세지를 주고 받는다고 하셨는데,그렇다면 만약에 레디스 서버가 다운이 된다면client2번은 메세지를 못 받게 되는건가요?이런 경우는 어떻게 대비 해야 하나요?레디스는 서버 한대에만 구성 해놓나요?
-
미해결웹소켓/STOMP 채팅서비스(spring, vue, redis)
수업자료 한번에 다운받기
수업자료 한번에 다운 받고 싶은데요. 압축파일로 올려주실수 있나요?