Campus
한 줄 소개
캠핑일상을 공유하고 캠핑을 해보고 싶을 때 취향이 맞는 사람들과 캠핑을 계획할 수 있는 서비스
기획 배경
-
코로나를 기점으로 캠핑인구가 급격하게 증가
-
캠핑을 가고 싶지만 장비가 없거나, 캠핑에 대한 지식이 부족한 사람들이 많음
-
비슷한 취향의 캠퍼들을 찾을 수 있는 서비스의 부재
-
캠핑장, 주변시설에 대한 정보의 부재
서비스 특징
-
사용자들은 본인의 취향에 맞는 캠핑 메이트들을 소개 받을 수 있다.
-
사용자들은 캠핑메이트를 모집하여 다른 사람들과 본인이 기획한 캠핑을 즐길 수 있다.
-
캠핑SNS를 통해 나만의 장소나 캠핑이야기 들을 공유할 수 있다.
-
캠핑장 소개를 통해 국내의 다양한 캠핑장 정보, 이용한 유저의 리뷰등을 알 수 있다.
프로젝트 팀원
프론트엔드(3) - 조해성, 곽동현, 김도훈 백엔드(2) - 최호준, 전양희
프로젝트 진행기간
- 2022.01.10 ~ 2022.02.18
주요 협업 툴
- JIRA
- MatterMost
- Gitlab
- Notion
기능 소개
메인 페이지
-
로그인, 로그아웃이 가능하다.
-
'지금뜨는 메이트'항목을 눌러 메이트 페이지로 이동이 가능하다.
채팅
-
현재 참가(참가 예정)인 메이트의 멤버들과 실시간 채팅이 가능하다.
-
타인의 프로필에서 채팅 버튼을 눌러 실시간 채팅을 시작할 수 있다.
메이트 매칭
-
나와 취향이 비슷한 캠퍼를 추천 받을 수 있다.
-
매칭 적합도를 알 수 있으며 해당 유저의 프로필로 이동이 가능하다.
메이트 모집
-
내가 원하는 취향의 캠핑에 참여할 수 있다.
-
캠핑 테마, 참가 인원의 대략적인 정보를 알 수 있다.
-
연령, 동행인원 제한에 적합할 경우 캠핑에 참여할 수 있다.
SNS 페이지
-
캠핑에 관련된 사진과 글을 공유할 수 있다.
-
내가 팔로우한 유저의 글을 우선적으로 볼 수 있다.
웹 소켓 코드
Vue
// 웹 소켓 연결(해당 채팅방의 번호를 인자로)
socketConnect(roomid){
// socket 연결
if (this.socketStop.includes(roomid) === false) {
// 고유 URL로 subscribe
let socket = new SockJS(`${SERVER_URL}/ws`)
this.stompClient = Stomp.over(socket)
// 메시지를 전달
this.stompClient.connect({}, frame=>{
this.stompClient.subscribe("/sub/"+roomid, res=>{
let jsonBody = JSON.parse(res.body)
let m={
'senderNickname':jsonBody.senderNickname,
'content': jsonBody.content,
'style': jsonBody.senderId == this.$store.state.userList.userNo ? "d-flex justify-content-end mb-4 my":'d-flex justify-content-start mb-4 your'
}
console.log(m)
this.MessageList.push(m)
})
}, err=>{
console.log("fail", err)
})
}
},
// 채팅방 확인 후 메시지 전송
sendMessage(){
const roomNo = this.temps.id
const roomTitle = this.temps.title
if(this.content.trim() !='' && this.stompClient!=null) {
let chatMessage = {
'title' : roomTitle,
'content': this.content,
'chatroomId' : roomNo,
'senderNickname':this.$store.state.userList.userNickname,
'senderId': this.$store.state.userList.userNo,
'id':"0"
}
this.stompClient.send("/pub/message", JSON.stringify(chatMessage),{})
this.content = ''
}
let MessageList = this.$refs.MessageList
MessageList.scrollTo({ top: MessageList.scrollHeight, behavior: 'smooth' });
SpringBoot
// 웹 소켓 연결
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/ws").setAllowedOriginPatterns("*").withSockJS();
}
@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.setApplicationDestinationPrefixes("/pub"); // publisher : message-handling methods로 라우팅됨
registry.enableSimpleBroker("/sub"); // subscriber : topic으로 시작되는 메시지가 메세지브로커로 라우팅됨
}
@Override
public void configureClientInboundChannel(ChannelRegistration registration) {
registration.interceptors(new MyChannelInterceptor());
}
}
// 웹 소켓 controller
public class MessageController {
private final IMessageService messageService;
private final SimpMessagingTemplate template;
@MessageMapping("/message")
public void sendMessage(@Payload Message chatMessage) {
log.info("전달 메세지 : " + chatMessage);
messageService.insertMessage(chatMessage);
template.convertAndSend("/sub/" + chatMessage.getChatroomId(), chatMessage);
}
}
// 웹 소켓 메세지 전송
public Message preSend(Message<?> message, MessageChannel channel) {
StompHeaderAccessor accessor = StompHeaderAccessor.wrap(message);
StompCommand command = accessor.getCommand();
if (command.compareTo(StompCommand.SUBSCRIBE) == 0) {
String destination = accessor.getDestination();
System.out.println("구독 주소 : " + destination);
System.out.println(message);
} else if (command.compareTo(StompCommand.CONNECT) == 0) {
System.out.println("사용자 연결");
} else if (command.compareTo(StompCommand.DISCONNECT) == 0) {
System.out.println("사용자 연결 해제");
}
return message;
}