728x90
이전글에 이어 간단하게 카카오톡 클론 코딩하는 방법에 대해 알아보겠습니다. 전체소스는 너무 길어서 핵심 소스만 작성하였습니다. 전체소스는 아래 Github 주소를 참고해 주세요. 소스에 부족한 부분이 많아, 세세한 부분은 본인 상황에 맞게 리펙토링 하여 사용하시면 될 것 같습니다.
- 환경설정 : [SpringBoot] Thymeleaf를 이용한 카카오톡 클론 코딩 - 1
- 서버 소스작성 : [SpringBoot] Thymeleaf를 이용한 카카오톡 클론 코딩 - 2
- 화면 소스작성 : [SpringBoot] Thymeleaf를 이용한 카카오톡 클론 코딩 - 3
- Github : 카카오톡 클론 코딩 Git
1. Config 작성
1.1. RedisConfig
@Configuration
public class RedisConfig {
@Value("${spring.data.redis.host}")
private String redisHost;
@Value("${spring.data.redis.port}")
private String redisPort;
@Value("${spring.data.redis.password}")
private String redisPassword;
@Bean
public RedisConnectionFactory redisConnectionFactory() {
RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration();
redisStandaloneConfiguration.setHostName(redisHost);
redisStandaloneConfiguration.setPort(Integer.parseInt(redisPort));
redisStandaloneConfiguration.setPassword(redisPassword);
LettuceConnectionFactory lettuceConnectionFactory = new LettuceConnectionFactory(redisStandaloneConfiguration);
return lettuceConnectionFactory;
}
@Bean
public RedisTemplate<String, Object> redisTemplate() {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory());
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new StringRedisSerializer());
return redisTemplate;
}
}
1.2. WebConfig
@Configuration
@EnableWebMvc
@RequiredArgsConstructor
public class WebConfig implements WebMvcConfigurer {
private final LoginCheckInterceptor loginCheckInterceptor;
@Value("${spring.file.profile-path}")
private String profilePath;
@Override
public void addResourceHandlers(final ResourceHandlerRegistry registry) {
registry.addResourceHandler("/**", "/resources/**")
.addResourceLocations("classpath:/templates/", "classpath:/static/");
registry.addResourceHandler("/profile/**")
.addResourceLocations(profilePath);
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loginCheckInterceptor)
.excludePathPatterns("/css/**/")
.excludePathPatterns("/js/**/")
.excludePathPatterns("/images/**/")
.excludePathPatterns("/profile/**/")
.excludePathPatterns("/favicon.ico")
.excludePathPatterns("/dudqls3kr/**/") // cafe24 URL
;
}
@Override
public void addViewControllers(ViewControllerRegistry registry) {
}
}
1.3. WebSockConfig
@Configuration
@EnableWebSocket
@RequiredArgsConstructor
public class WebSockConfig implements WebSocketConfigurer {
private final ChatHandler _chatHandler;
private final HttpSessionInterceptor _httpSessionInterceptor;
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(_chatHandler, "ws/chatting/{roomId}") // 웹소켓URL
.addInterceptors(_httpSessionInterceptor)
.setAllowedOriginPatterns("*");
}
}
1.4. ChatHandler
@Slf4j
@Component
@RequiredArgsConstructor
public class ChatHandler extends TextWebSocketHandler {
private static Map<String, List<WebSocketSession>> roomSessionsMap = new HashMap<>();
private final RedisService _redisService;
// 연결(클라이언트 접속)
@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
// 채팅방 ID 가져오기
String roomId = getRoomId(session);
// 채팅방 ID에 해당하는 세션 리스트 가져오기
List<WebSocketSession> roomSessions = roomSessionsMap.getOrDefault(roomId, new ArrayList<>());
// 세션 추가
roomSessions.add(session);
// 맵에 업데이트
roomSessionsMap.put(roomId, roomSessions);
String userId = "";
// 안읽은갯수만큼 읽음처리
try {
HttpSession httpSession = (HttpSession) session.getAttributes().get("httpSession");
userId = ((UserVO)httpSession.getAttribute(SessionKeys.USER_VO.name())).getLginData().getUserId();
_redisService.readMessage(roomId, userId);
}catch (Exception e){
}
log.info("클라이언트 접속 : [ " + userId + " ] : " + roomId);
}
// 연결 종료(클라이언트 접속 해제)
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
// 채팅방 ID 가져오기
String roomId = getRoomId(session);
// 채팅방 ID에 해당하는 세션 리스트 가져오기
List<WebSocketSession> roomSessions = roomSessionsMap.getOrDefault(roomId, new ArrayList<>());
// 세션 제거
roomSessions.remove(session);
// 맵에 업데이트
roomSessionsMap.put(roomId, roomSessions);
String userId = "";
// 안읽음처리
try{
HttpSession httpSession = (HttpSession) session.getAttributes().get("httpSession");
userId = ((UserVO)httpSession.getAttribute(SessionKeys.USER_VO.name())).getLginData().getUserId();
}catch (Exception e){
}
log.info("클라이언트 접속 해제 : [ " + userId + " ] : " + roomId);
}
// 메시지 수신
@Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
// 채팅방 ID 가져오기
String roomId = getRoomId(session);
// 채팅방 ID에 해당하는 세션 리스트 가져오기
List<WebSocketSession> roomSessions = roomSessionsMap.getOrDefault(roomId, new ArrayList<>());
// redis 에 정보입력
_redisService.saveMessage(roomId, message.getPayload());
ChatMessage chatMessage = ObjectUtil.jsonToObject(message.getPayload(), ChatMessage.class);
// 전체유저 - 현재접속 유저수 빼기
try{
HttpSession httpSession = (HttpSession) session.getAttributes().get("httpSession");
List<UserRoomInfo> userRoomList = ((UserVO)httpSession.getAttribute(SessionKeys.USER_VO.name())).getRoomList();
int totalUser = userRoomList.stream().filter(i -> i.getRoomId().equals(roomId))
.mapToInt(i -> Integer.parseInt(i.getMemberCount())).sum();
chatMessage.setUnread(String.valueOf(totalUser - roomSessions.size()));
}catch (Exception e){
}
TextMessage textMessage = new TextMessage(ObjectUtil.objectToJsonString(chatMessage));
// 메시지 전송
for (WebSocketSession webSocketSession : roomSessions) {
webSocketSession.sendMessage(textMessage);
}
}
// 오류 처리
@Override
public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
exception.printStackTrace();
}
// 세션에서 채팅방 ID 가져오기
private String getRoomId(WebSocketSession session) {
// WebSocket 연결 URL을 가져옴
String uri = session.getUri().toString();
// URI에서 채팅방 ID를 추출하여 반환
return uri.substring(uri.lastIndexOf("/") + 1);
}
// 실시간으로 메세지 전송하기
public void sendRealtimeMessageInRoom(String roomId, ChatMessage message) throws Exception {
// 채팅방 ID에 해당하는 세션 리스트 가져오기
List<WebSocketSession> roomSessions = roomSessionsMap.getOrDefault(roomId, new ArrayList<>());
for (WebSocketSession webSocketSession : roomSessions) {
TextMessage textMessage = new TextMessage(ObjectUtil.objectToJsonString(message));
webSocketSession.sendMessage(textMessage);
}
}
}
2. Controller 작성
2.1. ChatApiController
@RestController
@RequiredArgsConstructor
public class ChatApiController {
private final ChatService _chatService;
/**
* 채팅방 만들기
*/
@ResponseBody
@PostMapping(value = "/newChat")
@Operation(summary = "채팅방 만들기", description = "채방방 만들기")
public ChatUserJoinRes makeNewChat(@RequestBody @Validated ChatUserJoinReq params) {
return _chatService.makeNewChat(params);
}
/**
* 채팅방 나가기
*/
@ResponseBody
@PostMapping(value = "/exitRoom")
@Operation(summary = "채팅방 나가기", description = "채방방 나가기")
public BaseUpdateResponse exitRoom(@RequestBody @Validated ChatUserInfoReq params) {
return _chatService.exitRoom(params);
}
/**
* 채팅방 이름변경
*/
@ResponseBody
@PostMapping(value = "/updateRoomName")
@Operation(summary = "채팅방 이름변경", description = "채방방 이름변경")
public BaseUpdateResponse updateRoomName(@RequestBody @Validated RoomNameChangeReq params) {
return _chatService.updateRoomName(params);
}
/**
* 채팅방 초대하기
*/
@ResponseBody
@PostMapping(value = "/inviteRoom")
@Operation(summary = "채팅방 초대", description = "채방방 초대하기")
public BaseUpdateResponse inviteRoom(@RequestBody @Validated ChatUserInfoReq params) {
return _chatService.inviteRoom(params);
}
}
2.2. ChatViewController
@Controller
@RequiredArgsConstructor
public class ChatViewController {
private final ChatService _chatService;
private final RedisService _redisService;
@GetMapping(value = "/chats")
@LoginCheck(required = true)
public String chats(final Model model) {
UserVO userVO = SessionStore.getAs(SessionKeys.USER_VO, UserVO.class);
userVO.setRoomList(_redisService.getUserRoomList(userVO.getLginData().getUserId()));
SessionStore.put(SessionKeys.USER_VO, userVO);
model.addAttribute("userVO", SessionStore.getAs(SessionKeys.USER_VO, UserVO.class));
return "views/chat/chats";
}
@GetMapping(value = "/chat/{roomId}")
@LoginCheck(required = true)
public String chat(final Model model, @PathVariable String roomId) throws Exception {
model.addAttribute("roomVO", _chatService.getChatMessages(roomId));
model.addAttribute("userVO", SessionStore.getAs(SessionKeys.USER_VO, UserVO.class));
model.addAttribute("wsUrl", _chatService.getWsUrl());
return "views/chat/chat";
}
@GetMapping(value = "/find")
@LoginCheck(required = true)
public String find(final Model model) {
return "views/chat/find";
}
@GetMapping(value = "/more")
@LoginCheck(required = true)
public String more(final Model model) {
model.addAttribute("userVO", SessionStore.getAs(SessionKeys.USER_VO, UserVO.class));
return "views/chat/more";
}
@GetMapping(value = "/userProfile/{userId}")
@LoginCheck(required = true)
public String profile(final Model model, @PathVariable String userId) {
model.addAttribute("profileInfo", _chatService.getUserProfile(userId));
model.addAttribute("userVO", SessionStore.getAs(SessionKeys.USER_VO, UserVO.class));
return "views/chat/userProfile";
}
@GetMapping(value = "/roomInfo/{roomId}")
@LoginCheck(required = true)
public String roomInfo(final Model model, @PathVariable String roomId) {
model.addAttribute("roomInfo", _chatService.getRoomProfileInfo(roomId));
return "views/chat/roomInfo";
}
}
2.3. UserApiController
@RestController
@RequiredArgsConstructor
public class UserApiController {
private final UserService _userService;
@ResponseBody
@PostMapping(value = "/user/loginProc")
@Operation(summary = "로그인", description = "로그인 처리")
public UserVO userLoginProc(@RequestBody @Validated UserLginReq params) {
return _userService.userLoginProc(params);
}
@ResponseBody
@PostMapping(value = "/user/jnProc")
@Operation(summary = "회원가입", description = "회원가입 처리")
public BaseUpdateResponse userJnProc(@RequestBody @Validated UserRegsReq params) {
return _userService.userJnProc(params);
}
/**
* 친구추가하기
*/
@ResponseBody
@PostMapping(value = "/addFriend")
@Operation(summary = "친구추가", description = "친구추가하기")
public BaseUpdateResponse addFriend(@RequestBody @Validated AddFriendReq params) {
return _userService.addFriend(params);
}
/**
* 친구찾기
*/
@ResponseBody
@PostMapping(value = "/findFriend")
@Operation(summary = "친구찾기", description = "친구찾기")
public FindFriendRes findFriend(@RequestBody @Validated FindFriendReq params) {
return _userService.findFriend(params);
}
/**
* 즐겨찾기 업데이트
*/
@ResponseBody
@PostMapping(value = "/updateBookmark")
@Operation(summary = "즐겨찾기 변경", description = "즐겨찾기 변경")
public BaseUpdateResponse updateBookmark(@RequestBody @Validated UpdateBookmarkReq params) {
return _userService.updateBookmark(params);
}
/**
* 상태메세지 업데이트
*/
@ResponseBody
@PostMapping(value = "/updateStatMsg")
@Operation(summary = "상태메세지 변경", description = "상태메세지 변경")
public BaseUpdateResponse updateStatMsg(@RequestBody @Validated UpdateStatMsgReq params) {
return _userService.updateStatMsg(params);
}
@ResponseBody
@PostMapping(value = "/updateProfileImage")
@Operation(summary = "프로필사진 변경", description = "프로필사진 변경")
public BaseUpdateResponse updateProfileImage(@RequestParam MultipartFile file) {
return _userService.updateProfileImage(file);
}
}
2.4. UserViewController
@Slf4j
@Controller
public class UserViewController {
@RequestMapping(value = "/user/login", method = {RequestMethod.GET, RequestMethod.POST})
@Operation(summary = "로그인", description = "로그인 화면으로 이동")
public String userLogin(Model model) {
SessionUtil.removeUserVO();
return "views/user/login";
}
@GetMapping(value = "/user/chngPw")
@Operation(summary = "비밀번호 변경", description = "비밀번호 변경")
public String userChngPw(Model model) {
model.addAttribute("userId", "testUserId");
return "views/user/chngPw";
}
}
3. Service 작성
3.1. RedisService
@Service
@RequiredArgsConstructor
public class RedisService {
private final RoomDAO _roomDAO;
private final RedisTemplate<String, String> _redisTemplate;
/**
* 유저별 채팅방 정보 가져오기
*/
public List<UserRoomInfo> getUserRoomList(final String userId) {
// userId 로 현재 참여중인 방번호 찾기
List<UserRoomInfo> roomInfos = _roomDAO.selectRoomList(userId);
for (UserRoomInfo roomInfo : roomInfos) {
try {
JsonObject lastMsg = this.__getLast(this.__setRoomId(roomInfo.getRoomId()));
if (ObjectUtil.isNotEmpty(lastMsg)) {
roomInfo.setLastMsg(lastMsg.get("message").getAsString());
String time = lastMsg.get("time").getAsString();
String msgTime = "";
if (StringUtil.isNotEmpty(time)) {
msgTime = time.substring(2, 4) + "/" + time.substring(5, 7) + "/" + time.substring(8, 10) + " " +
time.substring(11, 13) + ":" + time.substring(14, 16);
}
roomInfo.setLastMsgDt(msgTime);
}
} catch (Exception e) {
}
}
return roomInfos;
}
/**
* 방번호별 대화내용 가져오기
*/
public List<ChatMessage> getRoomMessageList(final String roomId) throws Exception {
ArrayList<ChatMessage> chatMessages = new ArrayList<>();
List<String> messages = __getList(this.__setRoomId(roomId));
List<Object> unreadList = __getHashValue(__setUnReadKey(roomId));
for (String message : messages) {
// unread 카운트 추가
ChatMessage chatData = ObjectUtil.jsonToObject(message, ChatMessage.class);
chatData.setUnread("1");
chatMessages.add(chatData);
}
return chatMessages;
}
/**
* 대화내용 저장하기
* 새로운 대화방 만들기
*/
public boolean saveMessage(String roomId, String jsonString) {
try {
this.__appendToList(this.__setRoomId(roomId), jsonString);
return true;
} catch (Exception e) {
return false;
}
}
/**
* 방 없애기
*/
public boolean exitRoom(final String roomId) {
try {
_redisTemplate.delete(this.__setRoomId(roomId));
__deleteHashKey(this.__setRoomId(roomId));
return true;
} catch (Exception e) {
return false;
}
}
/**
* 방 나가기
*/
public boolean exitRoomUser(final String roomId, final String userId) {
try {
__deleteHashGroup(this.__setRoomId(roomId), userId);
return true;
} catch (Exception e) {
return false;
}
}
/**
* 메세지 읽음 처리
*/
public boolean readMessage(final String roomId, final String userId) {
try {
__setHash(__setUnReadKey(roomId), userId, "0");
return true;
} catch (Exception e) {
return false;
}
}
/*******************************************************************************************************************/
/*******************************************************************************************************************/
/**
* redis set
*/
private void __set(final String key, final String value, final boolean isOverride) {
ValueOperations<String, String> vop = _redisTemplate.opsForValue();
if (isOverride) {
vop.set(key, value);
} else {
String result = vop.get(key);
if (result != null && StringUtil.isNotEmpty(result)) {
// TODO : append ?
} else {
vop.set(key, value);
}
}
}
/**
* redis list append
*/
private void __appendToList(final String key, final String value) {
ListOperations<String, String> lop = _redisTemplate.opsForList();
lop.rightPush(key, value);
}
/**
* redis get value
*/
private String __getValue(final String key) {
ValueOperations<String, String> vop = _redisTemplate.opsForValue();
String value = vop.get(key);
return value;
}
private List<Object> __getHashValue(final String key) {
return _redisTemplate.opsForHash().values(key);
}
/**
* redis get list
*/
private List<String> __getList(final String key) {
return _redisTemplate.opsForList().range(key, 0, -1);
}
/**
* redis get list last data
*/
private JsonObject __getLast(final String key) throws Exception {
List<String> values = _redisTemplate.opsForList().range(key, -1, -1);
return (values != null && !values.isEmpty()) ? ObjectUtil.toJson(values.get(0)) : null;
}
/**
* redis get hash value(JSONObject)
*/
private JsonObject __getJson(final String key, final String group) {
return (JsonObject) _redisTemplate.opsForHash().get(key, group);
}
/**
* redis set hash
*/
private void __setHash(final String key, final String group, final String value) {
_redisTemplate.opsForHash().put(key, group, value);
}
/**
* redis delete hash group
*/
private void __deleteHashGroup(final String key, final String group) {
_redisTemplate.opsForHash().delete(key, group);
}
/**
* redis delete hash key
*/
private void __deleteHashKey(final String key) {
_redisTemplate.opsForHash().delete(key);
}
private String __setRoomId(String roomId) {
return "room:" + roomId + ":messages";
}
private String __setUnReadKey(String roomId) {
return "room:" + roomId + ":unread";
}
}
3.2. ChatService
@Slf4j
@Service
@RequiredArgsConstructor
public class ChatService {
private final RedisService _redisService;
private final UserService _userService;
private final ChatHandler _chatHandler;
private final UserDAO _userDao;
private final RoomDAO _roomDao;
@Value("${spring.websocket.path}")
private String _wsUrl;
/**
* 대화내용 불러오기
*/
public RoomVO getChatMessages(final String roomId) throws Exception {
RoomVO roomVO = new RoomVO();
// 방기본정보 가져오기
roomVO.setRoomInfo(_roomDao.selectRoomInfo(roomId));
// 방유저목록 가져오기
roomVO.setRoomMembers(_roomDao.selectMemberInfo(roomId));
// 메시지 목록 가져오기
roomVO.setMessageList(_redisService.getRoomMessageList(roomId));
return roomVO;
}
/**
* 프로필 불러오기
*/
public UserProfileInfo getUserProfile(final String userId) {
return _userDao.selectUserProfile(userId);
}
/**
* 채팅방 만들기
*/
@Transactional
public ChatUserJoinRes makeNewChat(ChatUserJoinReq params) {
String roomId = DateUtil.getDateStr("yyyyMMddHHmmssSSS");
Collections.sort(params.getUserIdList());
String userIds = String.join(",", params.getUserIdList());
String existRoomId = _roomDao.selectExistRoomId(userIds);
if (StringUtil.isNotEmpty(existRoomId)) {
roomId = existRoomId;
} else {
ChatMessage chatMessage = new ChatMessage();
chatMessage.setMessageId("system_" + roomId);
chatMessage.setTime(DateUtil.getDateStr());
chatMessage.setMessage(userIds + " 님이 입장하셨습니다.");
chatMessage.setUserId("system");
chatMessage.setUserName("system");
try {
_redisService.saveMessage(roomId, ObjectUtil.objectToJsonString(chatMessage));
} catch (Exception e) {
}
// room_info 입력 (room_id, room_name)
RoomInfo roomInfo = new RoomInfo();
roomInfo.setRoomId(roomId);
roomInfo.setRoomName(StringUtil.nvl(params.getRoomName(), userIds));
_roomDao.insertRoomInfo(roomInfo);
// room_member 입력 (room_id, member_id)
for (String userId : params.getUserIdList()) {
_roomDao.insertRoomMember(new RoomMember(roomId, userId));
}
}
return new ChatUserJoinRes(roomId);
}
/**
* 웹소켓 주소 가져오기
*/
public String getWsUrl() {
return this._wsUrl;
}
/**
* 채팅방 나가기
*/
@Transactional
public BaseUpdateResponse exitRoom(ChatUserInfoReq params) {
// 방 나가기
_roomDao.exitRoom(params);
// 방에 나 혼자 있으면 방자체를 삭제
int count = _roomDao.checkExistMember(params);
if (count == 0) {
// 방삭제
_roomDao.deleteRoomInfo(params);
// redis 대화내용 삭제
_redisService.exitRoom(params.getRoomId());
} else {
// 나갔다는 메세지 뿌려주기
ChatMessage chatMessage = new ChatMessage();
chatMessage.setMessageId("system_" + params.getRoomId());
chatMessage.setTime(DateUtil.getDateStr());
chatMessage.setMessage(params.getUserId() + " 님이 퇴장하셨습니다.");
chatMessage.setUserId("system");
chatMessage.setUserName("system");
try {
_chatHandler.sendRealtimeMessageInRoom(params.getRoomId(), chatMessage);
_redisService.saveMessage(params.getRoomId(), ObjectUtil.objectToJsonString(chatMessage));
_redisService.exitRoomUser(params.getRoomId(), params.getUserId());
} catch (Exception e) {
}
}
return new BaseUpdateResponse("Y", "방을 나왔습니다.");
}
/**
* 채팅방 프로필 정보 조회
*/
public RoomInfo getRoomProfileInfo(String roomId) {
return _roomDao.selectRoomProfileInfo(roomId);
}
/**
* 채팅방 이름 변경
*/
@Transactional
public BaseUpdateResponse updateRoomName(RoomNameChangeReq params) {
BaseUpdateResponse baseUpdateResponse = new BaseUpdateResponse();
_roomDao.updateRoomName(params);
baseUpdateResponse.setSuccYn("Y");
baseUpdateResponse.setMsg("채팅방 이름변경 완료하였습니다.");
return baseUpdateResponse;
}
/**
* 유저 초대하기
*/
@Transactional
public BaseUpdateResponse inviteRoom(ChatUserInfoReq params) {
BaseUpdateResponse baseUpdateResponse = new BaseUpdateResponse();
FindFriendReq findFriendReq = new FindFriendReq();
findFriendReq.setUserId(params.getUserId());
FindFriendRes findFriendRes = _userService.findFriend(findFriendReq);
if(findFriendRes.getExistYn().equals("Y")){
RoomMember roomMember = new RoomMember();
roomMember.setRoomId(params.getRoomId());
roomMember.setMemberId(params.getUserId());
_roomDao.insertRoomMember(roomMember);
ChatMessage chatMessage = new ChatMessage();
chatMessage.setMessageId("system_" + params.getRoomId());
chatMessage.setTime(DateUtil.getDateStr());
chatMessage.setMessage(params.getUserId() + " 님이 입장하셨습니다.");
chatMessage.setUserId("system");
chatMessage.setUserName("system");
try {
_chatHandler.sendRealtimeMessageInRoom(params.getRoomId(), chatMessage);
_redisService.saveMessage(params.getRoomId(), ObjectUtil.objectToJsonString(chatMessage));
} catch (Exception e) {
e.printStackTrace();
}
baseUpdateResponse.setSuccYn("Y");
baseUpdateResponse.setMsg("초대 완료하였습니다");
} else {
baseUpdateResponse.setSuccYn("N");
baseUpdateResponse.setMsg("유저가 존재하지 않습니다.");
}
return baseUpdateResponse;
}
}
3.3. UserService
@Service
@RequiredArgsConstructor
public class UserService {
private final UserDAO _userDAO;
private final RedisService _redisService;
@Value("${spring.file.profile-upload-path}")
private String _profilePath;
/**
* 로그인 처리
*/
@Transactional
public UserVO userLoginProc(final UserLginReq params) {
String userId = params.getUserId();
String userPw = params.getUserPw();
// 사용자 비밀번호 암호화
params.setUserPw(CryptoUtil.encrypt(userPw));
// 아이디, 비밀번호 확인 쿼리
UserLginInfo loginResult = _userDAO.selectLoginUser(params);
if (loginResult == null || ObjectUtil.isEmpty(loginResult)) {
// 오류 카운트 증가
_userDAO.updateErrorCount(params);
throw new YbBizException(ExceptType.LGIN002); // 아이디 또는 비밀번호를 잘못 입력하였습니다.
} else {
// 로그인 이력 쌓기
UserLginHistVO userLginHistVO = new UserLginHistVO(userId, HttpRequestUtil.getClientIP());
_userDAO.insertLoginUserHist(userLginHistVO);
if (loginResult.getPwErrCnt() >= 5) {
throw new YbBizException(ExceptType.LGIN003); // 비밀번호 5회 이상 잘못 입력하였습니다.
} else {
// 비밀번호 오류 카운트 초기화
_userDAO.updateErrorCountZero(params);
UserVO userVO = new UserVO();
// 사용자 정보 가져오기
this.__setLoginResult(userVO, loginResult);
// room 정보 가져오기
this.__setRoomList(userVO, userId);
// 친구목록 가져오기
this.__setFriendList(userVO, userId);
}
}
return SessionUtil.getUserVO();
}
private void __setLoginResult(UserVO userVO, UserLginInfo loginResult) {
userVO.setLginYn("Y");
userVO.setLginData(loginResult);
SessionUtil.setUserVO(userVO);
}
private void __setRoomList(UserVO userVO, String userId) {
List<UserRoomInfo> userRoomInfo = _redisService.getUserRoomList(userId);
userVO.setRoomList(userRoomInfo);
SessionUtil.setUserVO(userVO);
}
private void __setFriendList(UserVO userVO, String userId) {
List<UserFriendInfo> userFriendInfo = _userDAO.selectFriendList(userId);
userVO.setFriendList(userFriendInfo);
SessionUtil.setUserVO(userVO);
}
/**
* 회원가입 처리
*/
@Transactional
public BaseUpdateResponse userJnProc(final UserRegsReq params) {
BaseUpdateResponse res = new BaseUpdateResponse();
String userId = params.getUserId(); // 사용자 아이디
String userPw = params.getUserPw(); // 사용자 비밀번호
String userPwChk = params.getUserPwChk(); // 사용자 비밀번호 확인
// 비밀번호 동일 여부 체크
if (!userPw.equals(userPwChk)) {
throw new YbBizException(ExceptType.JOIN002); // 비밀번호가 일치 하지 않습니다.
} else {
userPw = CryptoUtil.encrypt(userPw);
params.setUserPw(userPw);
}
// 아이디 중복 체크
if (this.__idDupChk(userId)) {
throw new YbBizException(ExceptType.JOIN004); // 중복된 ID 입니다.
}
try {
_userDAO.insertUser(params);
} catch (Exception e) {
throw new YbBizException(ExceptType.JOIN001); // 회원가입에 실패하였습니다.
}
res.setSuccYn("Y");
return res;
}
/**
* 아이디 중복 체크
*/
private boolean __idDupChk(final String id) {
return _userDAO.dupChkUserId(id) > 0;
}
/**
* 친구추가
*/
@Transactional
public BaseUpdateResponse addFriend(final AddFriendReq params) {
BaseUpdateResponse res = new BaseUpdateResponse();
int count = _userDAO.selectFindFriend(params);
if (count > 0) {
res.setSuccYn("N");
res.setMsg("해당 유저는 이미 친구입니다.");
return res;
}
// 나와 친구 정보를 입력
_userDAO.insertFriend(params);
// 유저세션 수정
UserVO userVO = SessionStore.getAs(SessionKeys.USER_VO, UserVO.class);
this.__setFriendList(userVO, params.getUserId());
// 친구정보에도 나를 입력
AddFriendReq addFriendReq = new AddFriendReq();
addFriendReq.setUserId(params.getFriendId());
addFriendReq.setFriendId(params.getUserId());
_userDAO.insertFriend(addFriendReq);
res.setSuccYn("Y");
return res;
}
/**
* 친구찾기
*/
public FindFriendRes findFriend(final FindFriendReq params) {
return _userDAO.selectFindFriendId(params);
}
/**
* 즐겨찾기 업데이트
*/
@Transactional
public BaseUpdateResponse updateBookmark(final UpdateBookmarkReq params) {
BaseUpdateResponse res = new BaseUpdateResponse();
String type = "Y".equals(params.getBookmarkOnOff()) ? "등록" : "해제";
_userDAO.updateBookmark(params);
// 유저세션 업데이트
UserVO userVO = SessionStore.getAs(SessionKeys.USER_VO, UserVO.class);
this.__setFriendList(userVO, params.getUserId());
res.setSuccYn("Y");
res.setMsg("즐겨찾기 " + type + "처리가 완료되었습니다.");
return res;
}
/**
* 상태메세지 변경
*/
@Transactional
public BaseUpdateResponse updateStatMsg(final UpdateStatMsgReq params) {
BaseUpdateResponse res = new BaseUpdateResponse();
_userDAO.updateStatMsg(params.getStatMsg(), params.getUserId());
UserVO userVO = SessionStore.getAs(SessionKeys.USER_VO, UserVO.class);
userVO.getLginData().setStatMsg(params.getStatMsg());
SessionUtil.setUserVO(userVO);
res.setSuccYn("Y");
res.setMsg("상태메세지 변경이 완료되었습니다.");
return res;
}
/**
* 프로필사진 변경
*/
@Transactional
public BaseUpdateResponse updateProfileImage(MultipartFile file) {
BaseUpdateResponse res = new BaseUpdateResponse();
if (file != null && !file.isEmpty() && file.getSize() <= 102400) { // 100kb 이하
try {
String uploadDir = _profilePath;
String fileName = SessionStore.getAs(SessionKeys.USER_VO, UserVO.class).getLginData().getUserId() + ".png";
Path uploadPath = Paths.get(uploadDir);
if (!Files.exists(uploadPath)) {
Files.createDirectories(uploadPath);
}
Path filePath = uploadPath.resolve(fileName);
File targetFile = filePath.toFile();
file.transferTo(targetFile);
// 업로드 성공 메시지 설정
res.setSuccYn("Y");
res.setMsg("프로필 이미지가 성공적으로 업로드되었습니다.");
} catch (Exception e) {
res.setSuccYn("N");
res.setMsg(e.getMessage());
}
} else {
res.setSuccYn("N");
res.setMsg("허용가능한 용량(20kb)을 초과했습니다.");
}
return res;
}
}
다음 포스팅에선 화면 소스를 작성하도록 하겠습니다.
감사합니다.
728x90
'프레임워크 > SpringBoot' 카테고리의 다른 글
[SpringBoot] 외장톰캣 배포 404 오류 (0) | 2024.08.08 |
---|---|
[SpringBoot] Thymeleaf를 이용한 카카오톡 클론 코딩 - 3 (0) | 2024.08.07 |
[SpringBoot] Thymeleaf를 이용한 카카오톡 클론 코딩 - 1 (0) | 2024.08.07 |
[SpringBoot+Next.js] JWT 토큰 인증 예제 (0) | 2024.08.01 |
[SpringBoot] h2 consloe localhost에서 연결을 거부했습니다. (0) | 2024.07.31 |