묻고 답해요
150만명의 커뮤니티!! 함께 토론해봐요.
인프런 TOP Writers
순위 정보를
불러오고 있어요
-
해결됨CS 지식의 정석 | 디자인패턴 네트워크 운영체제 데이터베이스 자료구조
rest api
- 학습 관련 질문을 남겨주세요. 상세히 작성하면 더 좋아요! - 먼저 유사한 질문이 있었는지 검색해보세요. - 서로 예의를 지키며 존중하는 문화를 만들어가요. - 잠깐! 인프런 서비스 운영 관련 문의는 1:1 문의하기를 이용해주세요.면접에서 restful한 api에대해 설명하라고하면 rest api의 개념(네트워크 자료나온 rest api의 특징들)을 설명하면될까요?
-
미해결
채팅에 대해서 질문이 있습니다. ㅠㅠ
... // WebSocket 연결 및 STOMP 클라이언트 설정 useEffect(() => { const token = sessionStorage.getItem("accessToken"); // 세션에서 액세스 토큰을 가져옴 const socket = new SockJS(`http://localhost:8080/ws/chat`); // WebSocket 연결 const client = Stomp.over(socket); stompClientRef.current = client; // STOMP 연결 client.connect( { Authorization: `Bearer ${token}` }, (frame) => { setConnected(true); console.log("STOMP 연결 성공", frame); // 해당 채팅방에 대한 메시지 구독 client.subscribe(`/exchange/chat.exchange/room.${roomId}`, (msg) => { const receivedMessage = JSON.parse(msg.body); if (receivedMessage.type === "PLACE") { const place = JSON.parse(receivedMessage.message); setMessages((prev) => [...prev, { type: "PLACE", place }]); } else { setMessages((prev) => [...prev, receivedMessage]); } }); }, (error) => { console.error("STOMP 연결 실패:", error); alert("STOMP 연결 실패! 서버가 실행 중인지 확인하세요."); } ); // 채팅방 목록 가져오기 fetch(`${BASE_URL}/chat/rooms`, { headers: { Authorization: `Bearer ${token}`, }, }) .then((res) => res.json()) .then((data) => setChatRooms(data)) .catch((error) => console.error("채팅방 목록 가져오기 실패:", error)); return () => { if (client.connected) client.disconnect(); }; }, [roomId]); // 메시지 보내는 기능 const sendMessage = (message, type = "TALK") => { const token = localStorage.getItem("accessToken"); stompClientRef.current?.send( `/pub/chat.message.${roomId}`, { Authorization: `Bearer ${token}` }, JSON.stringify({ sender: currentUser, message, roomId, type, timestamp: new Date().toISOString(), }) ); if (type === "TALK") setNewMessage(""); }; ...리액트는 다음과 같이 구성하고 @Controller @RequiredArgsConstructor @Log4j2 public class ChatController implements ChatControllerDocs { private static final String CHAT_EXCHANGE_NAME = "chat.exchange"; private final static String CHAT_QUEUE_NAME = "chat.queue"; private final ChatService chatService; private final RabbitTemplate rabbitTemplate; @Override // 클라이언트에서 서버로 보낸 메시지를 메시지를 라우팅 // @MessageMapping("chat.message")로 설정하여 클라이언트로부터 /pub/chat.message 목적지로 전송된 STOMP 메시지를 처리한다. /*RabbitMQ*/ @MessageMapping("chat.message.{roomId}") /*STOMP*/ // @MessageMapping("/{roomId}") // 구독한 클라이언트에게 response를 제공할 url 정의 // @SendTo("/topic/{roomId}") public ResponseEntity<?> sendMessage( // @Payload: 메시지의 body를 정의한 객체에 매핑합니다. @Payload ChatMessageDTO message, // @DestinationVariable: 구독 및 메시징의 동적 url 변수를 설정. RestAPI의 @PathValue와 같다. @DestinationVariable int roomId) { try { ChatMessageDTO msg = chatService.sendMessage(message); log.info("Sent message: {}", msg); if (msg != null) { // RabbitMQ으로 메시지 전송 // template.convertAndSend() 메소드를 사용하여 메시지를 RabbitMQ로 전송한다. // 메시지는 chat.exchange로 전송되며, 라우팅 키는 room. + 메시지의 방 ID로 구성된다. rabbitTemplate.convertAndSend(CHAT_EXCHANGE_NAME, "room." + roomId, message); } else { log.error("Failed to create chat message. User might not be in the chat room. User: {}, Room: {}", message.getSender(), message.getRoomId()); } return ResponseEntity.ok().body(msg); } catch (Exception e) { log.error("Error processing message: ", e); throw new ChatException(e.getMessage()); } } @Configuration @EnableRabbit @RequiredArgsConstructor public class RabbitConfig { // Queue (큐): RabbitMQ에서 메시지를 저장하는 장소 private static final String CHAT_QUEUE_NAME = "chat.queue"; // Exchange (교환기): 메시지를 Queue로 라우팅(보내는) 역할 private static final String CHAT_EXCHANGE_NAME = "chat.exchange"; // Routing Key (라우팅 키): Exchange가 메시지를 어떤 Queue로 보낼지를 결정하는 데 사용 private static final String ROUTING_KEY = "room.*"; @Value("${spring.rabbitmq.host}") private String host; @Value("${spring.rabbitmq.port}") private int port; @Value("${spring.rabbitmq.username}") private String userName; @Value("${spring.rabbitmq.password}") private String password; // Queue 등록 @Bean public Queue queue() { return new Queue(CHAT_QUEUE_NAME, true); } // Exchange 등록 @Bean public TopicExchange exchange() { return new TopicExchange(CHAT_EXCHANGE_NAME); } // Exchange와 Queue바인딩 @Bean public Binding binding(Queue queue, TopicExchange exchange) { return BindingBuilder .bind(queue) .to(exchange) .with(ROUTING_KEY); } // RabbitMQ와의 메시지 통신을 담당하는 클래스 @Bean public RabbitTemplate rabbitTemplate() { RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory()); rabbitTemplate.setMessageConverter(jsonMessageConverter()); rabbitTemplate.setRoutingKey(ROUTING_KEY); return rabbitTemplate; } // RabbitMQ와의 연결을 관리하는 클래스 @Bean public ConnectionFactory connectionFactory() { CachingConnectionFactory factory = new CachingConnectionFactory(); factory.setHost(host); factory.setPort(port); factory.setVirtualHost("/"); factory.setUsername(userName); factory.setPassword(password); return factory; } // Queue를 구독(Subscribe)하는 걸 어떻게 처리하느냐에 따라 필요함. 당장은 없어도 됨. @Bean public SimpleRabbitListenerContainerFactory simpleRabbitListenerContainerFactory(ConnectionFactory connectionFactory, MessageConverter messageConverter) { SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory(); factory.setConnectionFactory(connectionFactory); factory.setMessageConverter(messageConverter); return factory; } @Bean public RabbitMessagingTemplate rabbitMessagingTemplate(RabbitTemplate rabbitTemplate) { return new RabbitMessagingTemplate(rabbitTemplate); } // 메시지를 JSON형식으로 직렬화하고 역직렬화하는데 사용되는 변환기 // RabbitMQ 메시지를 JSON형식으로 보내고 받을 수 있음 @Bean public Jackson2JsonMessageConverter jsonMessageConverter() { //LocalDateTime serializable을 위해 ObjectMapper objectMapper = new ObjectMapper(); objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, true); objectMapper.registerModule(dateTimeModule()); Jackson2JsonMessageConverter converter = new Jackson2JsonMessageConverter(objectMapper); return converter; } @Bean public Module dateTimeModule() { return new JavaTimeModule(); } }... @Override public void registerStompEndpoints(StompEndpointRegistry registry) { registry .setErrorHandler(stompExceptionHandler) // 소켓 연결 URI다. 소켓을 연결할 때 다음과 같은 통신이 이루어짐 .addEndpoint("/ws/chat") .setAllowedOriginPatterns("http://localhost:5173") // SocketJS를 통해 연결 지원 .withSockJS(); } @Override public void configureClientInboundChannel(ChannelRegistration registration) { log.info("--------------"); log.info("동작함"); registration.interceptors(stompHandler); } @Override public void configureMessageBroker(MessageBrokerRegistry registry) { // url을 chat/room/3 -> chat.room.3으로 참조하기 위한 설정 registry.setPathMatcher(new AntPathMatcher(".")); registry.setUserDestinationPrefix("/sub"); // 클라이언트 구독 경로 // RabbitMQ 브로커 리레이 설정 registry.enableStompBrokerRelay("/exchange", "/queue", "/topic") .setRelayHost(host) .setRelayPort(61613) .setClientLogin(userName) .setSystemPasscode(password) .setSystemLogin(userName) .setSystemPasscode(password); } ... }이렇게 구성했는데 rabbitMQ는 도커 컴포즈로 구성했습니다.근데 다음과 같은 문제가 발생했는데Chat.jsx:31 GET http://localhost:8080/ws/chat/285/4mxcvol4/jsonp?c=_jp.apvvzrr net::ERR_ABORTED 404 (Not Found)chat:1 Refused to execute script from 'http://localhost:8080/ws/chat/285/4mxcvol4/jsonp?c=_jp.apvvzrr' because its MIME type ('') is not executable, and strict MIME type checking is enabled.이렇게 계속해서 연결이 끊깁니다 어떻게 해야하나요?
-
해결됨[코드캠프] 부트캠프에서 만든 고농축 프론트엔드 코스
강사님 REST API와 GraphQL에 관하여 질문이 있습니다.
graphql의 장점이 딱 원하는 데이터만 가져온다는 것은 이해가 되는데, REST API도 딱 원하는 DB 컬럼만 요청해서 가져오거나 할 수도 있지 않나요? 이게 어떠한 차이가 있나 궁금합니다. ㅎㅎ
-
미해결모든 개발자를 위한 HTTP 웹 기본 지식
사용자, 관리자 Rest API를 한 서버에서 관리할 때, URI 설계
안녕하세요.이번에 신입 백엔드 개발자로 취업하게 되어 프로젝트를 진행 중에 해당 HTTP 강의를 듣고 궁금한 점이 생기어 올리게 되었습니다. 다름이 아니라 사용자 API와 관리자 API를 제공하는 서버가 분리되어 있지 않고 한 서버에서 모두 제공하는 형태에서 URI를 어떻게 설계해야 하는지 잘 모르겠습니다. 예를 들어, 로그인 API를 예시로 들어보자면 "/v1/api/login" 으로 만들면 깔끔한데 관리자 로그인도 들어가야해서 결국 "/v1/api/user/login", "/v1/api/admin/login" 으로 설계하게 되었는데 이게 잘 설계한 것인지 모르겠습니다... [예시]인증이 필요없는 API --> 기본 Path "/api/v1/..."--> ex. /api/v1/products (상품 목록 API) 사용자 인증이 필요한 API --> 기본 Path "/api/v1/user/..."--> ex. /api/v1/products (내 상품 목록 API) 관리자 인증이 필요한 API --> 기본 Path "/api/v1/admin/..."--> ex. /api/v1/admin/products (모든 유저 상품 목록 API) [궁금한 점]사용자 API와 관리자 API를 한 서버에서 제공하는 경우에 URI를 어떤 규칙을 갖고 설계하는 게 좋은지 궁금합니다. 하나의 컨트롤러에다 인증이 필요없는 Public API, 사용자 API, 관리자 API를 모두 관리하는 게 좋은지 아니면 권한별로 컨트롤러를 쪼개서 관리하는 게 좋은 것인지 궁금합니다. --> (하나의 컨트롤러로 관리) ex. ProductController--> (권한 별로 쪼개서 관리) ex. PublicProductController, UserProductController, AdminProductController
-
미해결
자꾸 Incorrect API key provided라고 뜹니다.
Chatgpt api 키 발급받고 intellij의 https request 켜서 키 복사 버튼 눌러서 제대로 붙어넣기 했는데 자꾸 401이 뜨면서 이런 에러가 나옵니다.POST https://api.openai.com/v1/completions Content-Type: application/json Authorization: Bearer <sk-proj-nawrjQjyZsY97JLPn96lT3BlbkFJwKvp4wAikQ2ENDrll4Kq> { "model": "text-davinci-001", "prompt": "1+1은 몇이야?", "temperature": 1, "max_tokens": 10 }{ "error": { "message": "Incorrect API key provided: <sk-proj**********************************************4Kq>. You can find your API key at https://platform.openai.com/account/api-keys.", "type": "invalid_request_error", "param": null, "code": "invalid_api_key" } }뭐가 문제일까요?
-
미해결
kafka로 restAPI통신
현재 카프카로 서버간의 restAPI 통신을 구현하려고 하는데요 예를들어 게시판 생성 요청을 예로 들어보겠습니다.게시판에는 멤버정보가 들어가는데요게시판 생성요청 정보에는게시판 제목게시판 내용 멤버 id 가 있구요 게시판 생성요청시에 작성자id 를 가지고 멤버 서버로 멤버 정보를 요청하는 방식을 해보려고 합니다. 카프카를 사용할때 1 . 게시판서버 게시판 생성메서드에서 프로듀서로 작성자id 를 특정 토픽에 보내고 2 . 멤버서버에서 컨슈머 리스너를 통해 해당 토픽에 요청정보가 들어오면 3 . 요청을 처리하고 다시 객체를 반환 해줘야 하는데 이것을 다시 프로듀서로 작성하고 4 . 게시판서버에서 리스너로 응답받은 객체를 생성메서드에 주입시켜 게시판을 완성시키려고 했습니다.찾아보니 컨슈머리스너는 void 이어야 한다고 하더라구요. 그렇게 되면 요청과 응답을 받는것을 어떻게 한 메서드 안에서 처리 해야 할까요 ?찾아본 방법으로는 kafka rest proxy 라는것도 찾아봤는데 현업에서 사용하는 방법이 따로 있는지 궁금합니다
-
미해결
서비스 계층에서 응답 객체 생성 관련 커뮤니티의 생각이 궁금합니다
안녕하세요.Spring Boot를 사용하여 REST API를 구축하는 과정에서 응답 객체의 생성 위치에 대한 베스트 프랙티스에 대해 커뮤니티의 의견을 듣고자 합니다.일반적으로, 컨트롤러는 HTTP 요청을 처리하고 HTTP 응답을 반환하는 역할을 수행하며, 서비스 계층은 비즈니스 로직과 데이터의 처리를 담당합니다. 그러나 서비스 계층에서 직접 HTTP 응답 객체를 생성하고 이를 컨트롤러를 통해 반환하는 방식에 대해 고민 중입니다.이 방법은 서비스 계층과 컨트롤러 계층 사이의 경계를 모호하게 만들 수 있으며, 서비스 계층의 재사용성과 테스트 용이성에 영향을 미칠 수 있습니다. 반면, 일부 개발자는 이러한 접근 방식이 효율적이라고 주장하기도 합니다.저는 다음과 같은 질문을 가지고 토론을 시작하고자 합니다:여러분의 프로젝트에서는 서비스 계층에서 응답 객체를 생성하나요, 아니면 컨트롤러에서만 생성하나요?각 방식의 장단점에 대해 어떻게 생각하시나요?응답 객체를 생성하는 위치에 대한 결정 기준은 무엇인가요?개발 프로세스의 투명성과 효율성을 높이는 것이 목표입니다. 여러분의 경험과 지식을 공유해 주신다면, 이를 바탕으로 더 나은 아키텍처 설계 결정을 내릴 수 있을 것 같습니다.감사합니다. github discussions (https://github.com/soobinJung/JCK-World/discussions/27 )
-
해결됨스프링 MVC 2편 - 백엔드 웹 개발 활용 기술
제대로 한게 맞는건가요?
@Transactional @Override public void updatePassword(String UUID, PasswordUpdateDto passwordUpdateDto) { User user = userRepository.findByUuid(UUID).orElseThrow(() -> new IllegalArgumentException("UUID정보 없음 = " + UUID)); user.hashPassword(passwordUpdateDto.getPassword()); }이게 서비스 코드인데 여기서 예외가 터지면 컨트롤러로 넘어가서 catch (IllegalArgumentException ex) { throw ex } 을 해서 처리하는게 맞게 하는건가요? @PutMapping("/user/update-password/{UUID}") public ResponseEntity<String> updatePassword(@PathVariable String UUID, @RequestBody PasswordUpdateInfo passwordUpdateInfo) { try { ModelMapper modelMapper = new ModelMapper(); PasswordUpdateDto passwordUpdateDto = modelMapper.map(passwordUpdateInfo,PasswordUpdateDto.class); userService.updatePassword(UUID,passwordUpdateDto); return ResponseEntity.ok("Password updated successfully"); } catch (IllegalArgumentException ex) { throw ex; } } @Slf4j @RestControllerAdvice(annotations = RestController.class) public class ExceptionController { @ExceptionHandler public ResponseEntity<ErrorResult> userExHandle(IllegalArgumentException e) { ErrorResult errorResult = new ErrorResult("USER-NOT-FIND", e.getMessage()); return new ResponseEntity<>(errorResult, HttpStatus.BAD_REQUEST); } } 포스트맨 쓰니까 일단 뜨긴뜨닙다
-
미해결
restAPI의 field-selection 개념 관련 질문드립니다!
안녕하세요! 강의 복습도 하면서 현업에 있는 직장인입니다ㅎㅎRestAPI를 구현중인데, 구현도 어렵지만 설계가 정말정말 어렵다고 느껴졉니다ㅠㅠ여러 레퍼런스를 찾아보면서 참고하여 설계를 해보고 있는데요,field selection 개념 도입하려는데 모호한 부분이 있어서요ㅠㅠ가령 GET /object 요청시 아래와 같이 Response가 왔을 경우,,{ "success": true, "message": "Data Found", "affectedRows": 1, "data": [ { "id": 1, "name": "test layer", "description": "test layer", "feature": { "type": "FeatureCollection", "features": [ { "type": "building", "properties": { "name": "object01" } } ] }, "sort_order": 0, "mod_date": "2023-03-09T06:52:19.000Z" } ] } GET field 파라미터로 가능한건 id, name, description, feature, sort_order, mod_date 까지인걸로 이해하고 있는데,만일 사용자가 features를 Return받고 싶다고 한다면이런 경우도 마찬가지로 field=feature로 받아서 처리하는게 개념적으로 맞을까요..?조언 부탁드립니다ㅜ.ㅜ
-
해결됨CS 지식의 정석 | 디자인패턴 네트워크 운영체제 데이터베이스 자료구조
REST API의 URI규칙에 대해서 질문드립니다
안녕하세요, 네트워크 파트의 REST API 강의 내용 수강 중 문의점이 생겨서 질문드립니다.설명해주신 내용 중에 'Uniform-Interface의 6개의 규칙'과 'URI 규칙 6가지'간의 관계가 어떻게 되는지 잘 이해가 안 되는데요,6개의 Uniform-Interface 규칙을 지켜서 URI를 만들었을 때의 'URI 생성 규칙'이 교안 187페이지에 나와있는 6가지 규칙이라는 게 맞을까요???
-
미해결
@Pathvariable에 엔티티의 식별자를 넣는 권장 방법이 있나요??
안녕하세요! REST API 공부를 하다가 요즘은 @Pathvariable로 엔티티의 식별자를 주는게 추세라는 말을 주워들었습니다. GET, DELETE 요청과 같이 requestbody를 사용하지 않는 요청에서는 식별자를 URI에 주는게 이해가 가는데 POST, PUT 요청은 requestbody에 데이터를 담아 보내는데 이 경우에도 식별자는 Pathvariable로 주는게 권장되는 방식인가요?? ex) 1번 방식은 userId를 경로에 포함 @PostMapping("/lists/{userId}") public void createCart(@PathVariable Long userId, @RequestBody CartDto cartDto) {} 2번 방식은 userId를 RequestBody에 포함 @PostMapping("/lists") public void createCart(@RequestBody CartDto cartDto) {}
-
미해결[개정판 2023-11-27] Spring Boot 3.x 를 이용한 RESTful Web Services 개발
restapi
restapi 통신에 대해 질문 있습니다 hetaoas를 사용하지 않고 restapi룰 만들어 보고 싶습니다. 이럴때는 objectmapper를 통해 json 변환하고 responseEntity로 통신해야할까요??
-
미해결[리뉴얼] React로 NodeBird SNS 만들기
제로초님 질문이 하나 있습니다.
제로초님의 강의를 필두로 개인 프로젝트를 진행하고있는데(구조가 완전 같습니다.) 백엔드 서버를 따로 만들지 않고 외부 rest api로 데이터를 saga쪽에서 axios로 받으려고 하는데 받아 지지가 않습니다. 혹시 왜 이런지 알 수 있을까요?
-
미해결모든 개발자를 위한 HTTP 웹 기본 지식
강사님 rest, http api 설계에 대해 궁금한점이 있습니다!
안녕하세요 김영한 강사님!! 이렇게 또 질문을 올리게 되었습니다..! 토이프로젝트로 api를 설계할때마다 너무나도 궁금한점들이 있었습니다. 간단한 예제로 회원 정보 api 설계로 예를 들어보겠습니다! . 회원 조회 api 설계라고 하면 보통 /members/{id} 이런식으로 알고 있습니다. . Spring Security를 이용해 jwt나 세션을 사용하면 Principal 객체에 회원 데이터가 들어있어서 이를 이용해 /members/me -> 이런 api 설계도 가능했었습니다 (jwt decode 된 정보나 세션 정보를 확인해서 정보조회) . 클라이언트 입장에선 로그인 후 회원 정보를 보고싶으면 회원조회를 요청하는 uri 주소를 띄워줘야 하는데 회원 리소스의 식별 값을 알아야지 조회가능 하지 않나? 라는 생각이 들었습니다. . Admin 페이지에서의 예를 들어보면 회원들 관리를 위해 전체 회원 목록 조회를 하면 회원 entity들을 dto로 반환하고 hateoas를 이용해 각각의 회원 정보를 조회하는 uri를 PagedResourcesAssembler를 이용해 각 회원마다 만들어 주었었습니다. (예시를 들자면 회원 목록 조회후 @Id 값들로 회원 조회, 게시판 글 목록 조회 후 @Id 값들로 게시판 글 조회, 주문 목록 조회후 @Id 값들로 주문 조회 이런식입니다!) ex) "id": 1, "username": member1, "links":[ { "rel": "회원 정보", "href": "http://localhost:8080/members/1" } ] "id": 2, "username": member2, "links":[ { "rel": "회원 정보", "href": "http://localhost:8080/members/2" }] ..... 하지만 로그인한 일반 유저 입장에선 -그냥 세션을 이용한다면 세션에 setAttribute한 값을 가져와서 회원을 조회해서 정보를 나타내주면 되고 -jwt를 이용한다면 decode된 정보를 바탕으로 회원을 조회해서 정보를 나타내주면 되는데 /members/{id} 로 api 설계를 하고 api 문서를 적어놨다고 하였을때 클라이언트 쪽에선 어떻게 개발을 하는지 궁금합니다. (보통 실무에선 restful을 지키기가 힘들기 때문에 보통 지켜지지 않으며 개발이 된다고 하신 답글을 보았었습니다!) . - /members/1 이라고 했을때 1번인걸 클라이언트가 어떻게 알고 회원 정보를 보는 페이지로 가는 uri를 적는걸까? -> 서버로 로그인 요청을 보내면 서버에서 1번 응답을 줘서 클라이언트에 따로 저장시켜놓는건가? . 이런 생각들입니다..! 제가 이부분에선 잘못 생각하고 있을수도 있지만 이상하게 헷갈리는 부분은 꼭 알고 가고싶어서 여쭤보게되었습니다. 구글에 찾아봐도 설계만 딱 있을뿐 저에게 답이 되는 부분은 찾지 못하였습니다..ㅜ . 그래서 정리하자면 1. /members/me -> Principal 에 저장된 정보로 회원조회 하는 설계랑 /members/{id} -> @PathVariable id 값으로 회원 조회 하는 설계가 명확히 어떤 측면에서 장단점들이 있고 제가 앞에서 말씀드린 내용의 대한 것과 제가 어떤 부분을 잘못 알고있는지 궁급합니다! 2. 보통 rest가 아닌 실무에서의 http api 설계를 할때 해당 페이지에서 이동 가능한 link 까지 포함해서 응답을 주는지 궁금합니다! (회원 정보 페이지 라고 한다면 회원 정보 페이지에선 회원 수정, 회원 탈퇴 의 링크가 있다면 이걸 포함시켜서 응답을 해야하는지)
주간 인기글
순위 정보를
불러오고 있어요