๋ชฉ์ฐจ
WebSocket ์ ์จ๋ณด์
ํ๋ก์ ํธ์์ ์ค์๊ฐ ์์น์ ๋ณด๋ฅผ ๊ณต์ ํ๋ ๊ธฐ๋ฅ์ ๊ฐ๋ฐํ๊ธฐ๋ก ํ๋ค.
โ
์น์์ผ์ ์ ์ํ๋ฉด ์ ์ํ ์ ์ ๋ฆฌ์คํธ๋ฅผ ๊ฐ์ ธ์จ๋ค.
โ ์ฌ์ฉ์ ์์น๊ฐ ๋ณ๊ฒฝ๋ ๋๋ง๋ค ์์ผ์ผ๋ก ์์น์ ๋ณด๋ฅผ ๋ณด๋ด๋ฉด, ๋ค๋ฅธ ์ฌ์ฉ์์๊ฒ๋ ํด๋น ์ฌ์ฉ์์ ์์น์ ๋ณด๋ฅผ ๋ณผ ์ ์๋ค.
โ ์ด๋, ์์ผ์ผ๋ก userId ๋ฅผ ๋ณด๋ด๋ฉด ์๋์ผ๋ก ์ฌ์ฉ์ ๋ฐ์ดํฐ๋ฅผ ๋ถ๋ฌ์ค๊ณ ๋ฐ์์จ ์์น(์๋,๊ฒฝ๋)๋ ํจ๊ป ํฌํจํด์ ๋ค๋ฅธ ์์ผ ์ ์ ๋ค์๊ฒ ๋ณด๋ธ๋ค.
์ค์๊ฐ ์์น์ ๋ณด๋ฅผ ๊ตฌํํ๊ธฐ ์ํด์๋ ๊ณ์ํด์ ์์น๋ฐ์ดํฐ๋ฅผ ๋ณด๋ด๋ ๊ณผ์ ์ด ํ์ํ๋ฐ...
์ด๊ฑธ HTTP ๋ก ๊ตฌํ ํ๊ธฐ์๋ ๋งค์ฐ ํ๋ค๋ค. HTTP ์ ํน์ง ๋๋ฌธ์ธ๋ฐ, ๋จ๋ฐฉํฅํต์ ์ธ ์ (์์ฒญ์ ํด์ผ๋ง ์๋ต์ ๋ณด๋ผ ์ ์์)
์์ฒญ์ ํ ๋๋ง๋ค ์๋กญ๊ฒ ์ฐ๊ฒฐํด์ผํ๋ ๋ฑ ๋ง์ ๋จ์ ์ด ์๋ค. ์๋ฐฉํฅ ํต์ ์ ๊ตฌํํ๊ธฐ ์ํด์ ํ๋ง์ด๋ผ๋ ๊ฐ๋ ์ด ๋์ค๊ธด ํ๋๋ฐ ์ด๊ฑด ์ฃผ๊ธฐ์ ์ผ๋ก ์๋ฒ์ ์์ฒญ์ ๋ณด๋ด๋ ๋ฐฉ๋ฒ์ผ๋ก ๊ตฌํํ๋ค. ํ์ง๋ง ์ค๋ฒํค๋๊ฐ ๋๋ฌด ์ฌํ๊ณ , ์ค์๊ฐ ์ฑ๋ ๋ฎ๋ค.
๊ทธ๋์ ์ฐ๋ฆฌ๋ WebSocket ์ ์ด๋ค. ์น์์ผ์ ํ๋ฒ ์ฐ๊ฒฐํ๋ฉด ๊ณ์ํด์ ์ฐ๊ฒฐ์ด ์ ์ง๋๋ฉฐ, ์๋ฐฉํฅ ํต์ (์ ์ด์ค ํต์ )์ด ๊ฐ๋ฅํ๋ค. ๋ํ ์์ฒญ์ด ์๋๋ผ๋ ์๋ฒ์์ ํด๋ผ์ด์ธํธ๋ก ์๋ต์ ๋ณด๋ผ ์ ์๊ณ ์ค๋ฒํค๋๋ ์ ๊ธฐ ๋๋ฌธ์ ์ค์๊ฐ์ด ํ์ํ ์์คํ ์์๋ ์น์์ผ์ ์ฐ๊ฒ๋๋ค ! ๐
WebSocket ์น ์์ผ ๊ฐ๋
HTTPํด๋ผ์ด์ธํธ๊ฐ ์์ฒญ(Request) → ์๋ฒ๊ฐ ์๋ต(Response)์๋ต ํ ์ฐ๊ฒฐ์ด ๋ซํ (Stateless)์๋ก์ด ์์ฒญ๋ง๋ค ๋ค์ ์ฐ๊ฒฐ ํ์์ฃผ๋ก ๋จ๋ฐฉํฅ ํต์ (ํด๋ผ์ด์ธํธ → ์๋ฒ)๋ฐ์ดํฐ๋ฅผ ํค๋์ ํฌํจ์์ผ์ ์ ๋ฌํจ ํด
hyejux.tistory.com
build gradle ์ถ๊ฐ
config ์ค์ ๊ตฌํ
์น์์ผ์ ์ฐ๊ธฐ ์ํด์๋ Config (์น ์์ผ ์ค์ ํ์ผ) ๊ณผ Handler(์์ผ ์ด๋ฒคํธ ์ฒ๋ฆฌ) ํ์ผ์ ๋ง๋ค์ด์ค์ผํ๋ค.
`config.java`
์น ์์ผ ํ์ฑํ, ์๋ํฌ์ธํธ ์ค์
`WebSocketConfigurer` ๊ตฌํ
`@EnableWebSocket ` ์น ์์ผ ํ์ฑํ ์ด๋ ธํ ์ด์
package com.sanchat.api.config;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
@Configuration // ์คํ๋ง์ค์ ํ์ผ
@EnableWebSocket // ์์ผ ์ฌ์ฉ
@RequiredArgsConstructor
public class MapWebSocketConfig implements WebSocketConfigurer {
private final MapWebSocketHandler mapWebSocketHandler;
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(mapWebSocketHandler, "/tracking") // ์์ฒญ ํ์ฉ ๊ฒฝ๋ก
.setAllowedOrigins("*"); // ์๋ฒ๊ฐ ๋ฌ๋ผ๋ ํ์ฉ Cors
}
}
Handler ํธ๋ค๋ฌ ๊ตฌํ
`Handler.java`
package com.sanchat.api.config;
import ch.qos.logback.core.net.SyslogOutputStream;
import ch.qos.logback.core.net.server.Client;
import com.fasterxml.jackson.databind.util.JSONPObject;
import com.sanchat.api.dto.DogDTO;
import com.sanchat.api.dto.UserDTO;
import com.sanchat.api.dto.UserMDTO;
import com.sanchat.api.service.UserService;
import lombok.Builder;
import org.apache.catalina.User;
import org.apache.tomcat.util.json.JSONParser;
import org.json.JSONException;
import org.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.*;
import org.springframework.web.socket.handler.TextWebSocketHandler;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
@Component // bean ์ผ๋ก ๋ฑ๋ก
public class MapWebSocketHandler extends TextWebSocketHandler {
@Autowired
UserService userService;
/**
* ์ฌ์ฉ์ ์ ๋ณด๋ฅผ ์ ์ฅํ๋ MAP (์ ์ํ ์ฌ์ฉ์ ๋ฆฌ์คํธ)
* userID , UserMDTO ์ ์ฅ
*/
Map<String, UserMDTO> userList = new HashMap<>();
/**
* ํ์ฌ ์ ์ํ ํด๋ผ์ด์ธํธ (WebSocket ์ธ์
) ๋ชฉ๋ก ์ ์ฅ
*/
private static final ConcurrentHashMap<String, WebSocketSession> CLIENTS =
new ConcurrentHashMap<String, WebSocketSession>();
/**
* ํด๋ผ์ด์ธํธ๊ฐ ์น์์ผ ์ฐ๊ฒฐ์ ์ฑ๊ณต ์ ํธ์ถ
*/
@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
CLIENTS.put(session.getId(), session);
// ํ์ฌ ์ ์ ์ค์ธ ์ฌ์ฉ์ ๋ชฉ๋ก์ JSON์ผ๋ก ๋ณํ
JSONObject response = new JSONObject();
response.put("type", "USER_LIST"); // ๋ฉ์์ง ํ์
์ USER_LIST๋ก ์ค์
List<JSONObject> userArray = new ArrayList<>();
for (UserMDTO user : userList.values()) {
System.out.println(user.getUserId() + session.getId());
JSONObject userJson = new JSONObject();
userJson.put("userId", user.getUserId());
userJson.put("userName", user.getUserName());
userJson.put("photo", user.getPhoto());
userJson.put("userIntro", user.getUserIntro());
userJson.put("latitude", user.getLatitude());
userJson.put("longitude", user.getLongitude());
userJson.put("dogList", user.getDogList());
userArray.add(userJson);
}
response.put("users", userArray);
// ์๋ก ์ ์ํ ์ฌ์ฉ์์๊ฒ ํ์ฌ ์ ์ ์ค์ธ ์ฌ์ฉ์ ๋ชฉ๋ก ์ ์ก
session.sendMessage(new TextMessage(response.toString()));
}
/**
* ํด๋ผ์ด์ธํธ๊ฐ ๋ฉ์ธ์ง๋ฅผ ๋ณด๋ผ ๋ ํธ์ถ
* (์ฌ์ฉ์ ์์น ์ ์ฅ, ๋ค๋ฅธ ํด๋ผ์ด์ธํธ์ ๋ฉ์ธ์ง ์ ๋ฌ)
*/
@Override
public void handleMessage(WebSocketSession session, WebSocketMessage<?> message) throws Exception {
String id = session.getId(); //๋ฉ์์ง๋ฅผ ๋ณด๋ธ ์น์์ผ ์ธ์
์์ด๋
// ๋ฐ์ ๋ฉ์์ง๋ฅผ JSON ํ์์ผ๋ก ๋ณํ
JSONObject jsonObject = new JSONObject(String.valueOf(message.getPayload()));
// ํ์ํ ์ ๋ณด ์ถ์ถ
String userId = jsonObject.getString("userId");
String latitude = jsonObject.getString("latitude");
String longitude = jsonObject.getString("longitude");
String type = jsonObject.getString("type"); // LOCATION, CLOSE , USER_LIST..
UserDTO userDTO = userService.getUser(userId); // ์ฌ์ฉ์ ์ ๋ณด ์กฐํํด์ค๊ธฐ
// ์ฌ์ฉ์ ์์น ์ ๋ณด๋ฅผ ํฌํจํ๋ UserDTO ๊ฐ์ฒด ์์ฑ
UserMDTO userMDTO = new UserMDTO();
userMDTO.setUserId(userId);
userMDTO.setPhoto(userDTO.getPhoto().getPhotoUrl());
userMDTO.setUserName(userDTO.getUserName());
userMDTO.setUserIntro(userDTO.getUserIntro());
List<String> dogList = new ArrayList<>();
for(DogDTO dDto : userDTO.getDogList()){
dogList.add(dDto.getDogName());
}
userMDTO.setDogList(dogList);
// ์๋, ๊ฒฝ๋
userMDTO.setLatitude(Double.parseDouble(latitude));
userMDTO.setLongitude(Double.parseDouble(longitude));
// ์ฌ์ฉ์ ์ ๋ณด๋ฅผ userList(Map) ์ ์ ์ฅ
userList.put(session.getId(), userMDTO);
System.out.println(id +" :: " + userId + " ๋์ ์์น ์ ๋ณด | " +latitude + " | " +longitude + type );
// ๋ฉ์ธ์ง type = "CLOSE" ๋ฉด ์ฐ๊ฒฐ ์ข
๋ฃ, ์ธ์
์ ๊ฑฐ
switch(type) {
case "CLOSE" :
CLIENTS.remove(session.getId());
userList.remove(session.getId());
break;
default:
break;
}
// ํ์ ์ ์ ์ค์ธ ๋ชจ๋ ํด๋ผ์ด์ธํธ์๊ฒ ๋ฉ์์ง ์ ๋ฌ (์์ ์ ์ ์ธ)
CLIENTS.entrySet().forEach(arg -> {
if(!arg.getKey().equals(id)) { // ์์ ์ด ์๋๋ฉด ๋ฉ์์ง๋ฅผ ์ ๋ฌํฉ๋๋ค.
try {
// ์์น ์ ๋ณด๋ ํฌํจํ์ฌ ์ ๋ฌ
JSONObject response = new JSONObject();
response.put("type", "LOCATION"); // ๋ฉ์์ง ํ์
์ LOCATION
response.put("userId", userMDTO.getUserId());
response.put("userName", userMDTO.getUserName());
response.put("latitude", userMDTO.getLatitude());
response.put("longitude", userMDTO.getLongitude());
response.put("photo", userMDTO.getPhoto());
response.put("dogList", userMDTO.getDogList());
// ํด๋น ํด๋ผ์ด์ธํธ์๊ฒ ๋ฉ์์ง ์ ์ก
arg.getValue().sendMessage(new TextMessage(response.toString()));
} catch (IOException | JSONException e) {
e.printStackTrace();
}
}
});
}
/**
* WebSocket ์ ์ก ์ค ์๋ฌ ๋ฐ์ ์ ํธ์ถ๋จ
*/
@Override
public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
}
/**
* ํด๋ผ์ด์ธํธ์์ ์ฐ๊ฒฐ์ด ์ข
๋ฃ๋ ๋ ํธ์ถ๋จ
* - ํด๋น ํด๋ผ์ด์ธํธ๋ฅผ CLIENTS ๋ชฉ๋ก์์ ์ ๊ฑฐ
*/
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception {
CLIENTS.remove(session.getId());
userList.remove(session.getId());
}
/**
* ๋ถ๋ถ ๋ฉ์์ง๋ฅผ ์ง์ํ ์ง ์ฌ๋ถ๋ฅผ ๋ฐํ (ํ์ฌ๋ false)
*/
@Override
public boolean supportsPartialMessages() {
return false;
}
}
`TextWebSocketHandler` : ํ ์คํธ ๋ฉ์์ง๋ฅผ ์ฒ๋ฆฌํ๋ ์น์์ผ ํธ๋ค๋ฌ
- `afterConnectionEstablished()` : ํด๋ผ์ด์ธํธ๊ฐ ์น์์ผ์ ์ฐ๊ฒฐ๋ ๋ ์คํ
- `handleTextMessage()` : ํด๋ผ์ด์ธํธ๊ฐ ๋ฉ์์ง๋ฅผ ๋ณด๋ผ ๋ ์คํ
- `afterConnectionClosed()` : ํด๋ผ์ด์ธํธ๊ฐ ์ฐ๊ฒฐ์ ์ข ๋ฃํ ๋ ์คํ
์ ์ธ๊ฐ์ ๋ฉ์๋๊ฐ ์ฃผ๋ก ์ฌ์ฉ๋๋ค.
/**
* ์ฌ์ฉ์ ์ ๋ณด๋ฅผ ์ ์ฅํ๋ MAP (์ ์ํ ์ฌ์ฉ์ ๋ฆฌ์คํธ)
* userID , UserMDTO ์ ์ฅ
*/
Map<String, UserMDTO> userList = new HashMap<>();
Map ์ ์ด์ฉํด์ ์ ์ํ ์ ์ ๋ค์ ์ต์ ์์น๋ฐ์ดํฐ๋ฅผ ๊ด๋ฆฌํ๋ค. key ๋ sesstionId
/**
* ํด๋ผ์ด์ธํธ๊ฐ ์น์์ผ ์ฐ๊ฒฐ์ ์ฑ๊ณต ์ ํธ์ถ
*/
@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
CLIENTS.put(session.getId(), session); // ํด๋ผ์ด์ธํธ ์ธ์
์ ์ ์ฅ
// ํ์ฌ ์ ์ ์ค์ธ ์ฌ์ฉ์ ๋ชฉ๋ก์ JSON์ผ๋ก ๋ณํ
JSONObject response = new JSONObject();
response.put("type", "USER_LIST"); // ๋ฉ์์ง ํ์
์ USER_LIST๋ก ์ค์
List<JSONObject> userArray = new ArrayList<>();
for (UserMDTO user : userList.values()) {
System.out.println(user.getUserId() + session.getId());
JSONObject userJson = new JSONObject();
userJson.put("userId", user.getUserId());
userJson.put("userName", user.getUserName());
userJson.put("photo", user.getPhoto());
userJson.put("userIntro", user.getUserIntro());
userJson.put("latitude", user.getLatitude());
userJson.put("longitude", user.getLongitude());
userJson.put("dogList", user.getDogList());
userArray.add(userJson);
}
response.put("users", userArray);
// ์๋ก ์ ์ํ ์ฌ์ฉ์์๊ฒ ํ์ฌ ์ ์ ์ค์ธ ์ฌ์ฉ์ ๋ชฉ๋ก ์ ์ก
session.sendMessage(new TextMessage(response.toString()));
}
ํด๋ผ์ด์ธํธ๊ฐ ์น ์์ผ ์ฐ๊ฒฐ์ ์ฑ๊ณตํ๋ฉด, ์์ผ์ ์ ์ ํด ์๋ ์ ์ ๋ฆฌ์คํธ๋ฅผ JSON ํ์ ์ผ๋ก ๋ณํํด์ ํด๋น ํด๋ผ์ด์ธํธ์๊ฒ ์ ์กํ๋ค.
String id = session.getId(); //๋ฉ์์ง๋ฅผ ๋ณด๋ธ ์น์์ผ ์ธ์
์์ด๋
// ๋ฐ์ ๋ฉ์์ง๋ฅผ JSON ํ์์ผ๋ก ๋ณํ
JSONObject jsonObject = new JSONObject(String.valueOf(message.getPayload()));
// ํ์ํ ์ ๋ณด ์ถ์ถ
String userId = jsonObject.getString("userId");
String latitude = jsonObject.getString("latitude");
String longitude = jsonObject.getString("longitude");
String type = jsonObject.getString("type"); // LOCATION, CLOSE , USER_LIST..
UserDTO userDTO = userService.getUser(userId); // ์ฌ์ฉ์ ์ ๋ณด ์กฐํํด์ค๊ธฐ
// ์ฌ์ฉ์ ์์น ์ ๋ณด๋ฅผ ํฌํจํ๋ UserDTO ๊ฐ์ฒด ์์ฑ
UserMDTO userMDTO = new UserMDTO();
userMDTO.setUserId(userId);
userMDTO.setPhoto(userDTO.getPhoto().getPhotoUrl());
userMDTO.setUserName(userDTO.getUserName());
userMDTO.setUserIntro(userDTO.getUserIntro());
List<String> dogList = new ArrayList<>();
for(DogDTO dDto : userDTO.getDogList()){
dogList.add(dDto.getDogName());
}
userMDTO.setDogList(dogList);
// ์๋, ๊ฒฝ๋
userMDTO.setLatitude(Double.parseDouble(latitude));
userMDTO.setLongitude(Double.parseDouble(longitude));
ํด๋ผ์ด์ธํธ๊ฐ ๋ฉ์ธ์ง๋ฅผ ๋ณด๋ผ๋๋ง๋ค ์คํ ์ฝ๋์ด๋ค.
๋ฐ์ดํฐ๋ฅผ ๊ธฐ๋ฐ์ผ๋ก (์ฌ์ฉ์์ ๋ณด+์์น(์๋,๊ฒฝ๋)) ๋ฐ์ดํฐ๋ฅผ ๋ง๋ ๋ค.
// ์ฌ์ฉ์ ์ ๋ณด๋ฅผ userList(Map) ์ ์ ์ฅ
userList.put(session.getId(), userMDTO);
// ํ์ ์ ์ ์ค์ธ ๋ชจ๋ ํด๋ผ์ด์ธํธ์๊ฒ ๋ฉ์์ง ์ ๋ฌ (์์ ์ ์ ์ธ)
CLIENTS.entrySet().forEach(arg -> {
if(!arg.getKey().equals(id)) { // ์์ ์ด ์๋๋ฉด ๋ฉ์์ง๋ฅผ ์ ๋ฌํฉ๋๋ค.
try {
// ์์น ์ ๋ณด๋ ํฌํจํ์ฌ ์ ๋ฌ
JSONObject response = new JSONObject();
response.put("type", "LOCATION"); // ๋ฉ์์ง ํ์
์ LOCATION
response.put("userId", userMDTO.getUserId());
response.put("userName", userMDTO.getUserName());
response.put("latitude", userMDTO.getLatitude());
response.put("longitude", userMDTO.getLongitude());
response.put("photo", userMDTO.getPhoto());
response.put("dogList", userMDTO.getDogList());
// ํด๋น ํด๋ผ์ด์ธํธ์๊ฒ ๋ฉ์์ง ์ ์ก
arg.getValue().sendMessage(new TextMessage(response.toString()));
} catch (IOException | JSONException e) {
e.printStackTrace();
}
}
});
-> userList ์๋ ์ถ๊ฐํ๊ณ
-> JSON ํ์ ์ผ๋ก ๋ณํํด์ ๋๋ฅผ ์ ์ธํ ๋ค๋ฅธ ํด๋ผ์ด์ธํธ๋ค์๊ฒ ๋ฉ์ธ์ง๋ฅผ ๋ณด๋ธ๋ค.
// ๋ฉ์ธ์ง type = "CLOSE" ๋ฉด ์ฐ๊ฒฐ ์ข
๋ฃ, ์ธ์
์ ๊ฑฐ
switch(type) {
case "CLOSE" :
CLIENTS.remove(session.getId());
userList.remove(session.getId());
break;
default:
break;
}
/**
* ํด๋ผ์ด์ธํธ์์ ์ฐ๊ฒฐ์ด ์ข
๋ฃ๋ ๋ ํธ์ถ๋จ
* - ํด๋น ํด๋ผ์ด์ธํธ๋ฅผ CLIENTS ๋ชฉ๋ก์์ ์ ๊ฑฐ
*/
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception {
CLIENTS.remove(session.getId());
userList.remove(session.getId());
}
- ๋ฉ์ธ์ง ํ์ ์ด CLOSE ์ผ๋์๋ ์ธ์ ์ ์ ๊ฑฐํด์ ์ฐ๊ฒฐ์ ์ข ๋ฃ์ํค๊ณ
- `afterConnectionClosed` ์๋ ์ธ์ ์ ๊ฑฐ ์ฝ๋๋ฅผ ์์ฑํด์ค๋ค => ์ฐ๊ฒฐ์ด ์ข ๋ฃ๋ ํ ํธ์ถ๋จ
ํด๋ผ์ด์ธํธ ์ฐ๊ฒฐ
ํ ์คํธ๋ฅผ ํ๊ธฐ ์ํด์ ํฌ๋กฌ ํ์ฅํ๋ก๊ทธ๋จ์ธ `Browser WebSocket Client` ๋ฅผ ์ฌ์ฉํ๋ค.
ํด๋ผ์ด์ธํธ์์๋ ์ค์ ํด๋ endpoint ์ฃผ์๋ก ์์ฒญ์ ๋ณด๋ด๋ฉด ์น์์ผ์ ์ฐ๊ฒฐ๋๋ค
โ ํ ์คํธ 1) ์์ผ์ ์ ์ํ ์ ์ ์ ๋ณด ๊ฐ์ ธ์ค๊ธฐ
1) arin84 ๊ฐ ์ ์ํ๋ฉด -> ํ์ฌ ์์ผ์ ์๋ ์ ์ ๋ฆฌ์คํธ (arin84 ๊ฐ ์ฒ์์ด๋ฏ๋ก ์๋ฌด๋ ์์)
2) ์ดํ ์์น์ ๋ณด๋ฅผ ์๋ฒ๋ก ๋ณด๋ธ๋ค.
3) doyoon90 ์ด ์์ผ์ ์ ์ํ๋ฉด ๋ฏธ๋ฆฌ ์ ์ํด ์๋ arin84์ ์ ๋ณด๊ฐ ๋ฌ๋ค.
4) ์ดํ ์์น์ ๋ณด๋ฅผ ์๋ฒ๋ก ๋ณด๋ธ๋ค.
โ
ํ
์คํธ 2 ) ์์ผ์ ์ ์ํ ์ฌ๋์๊ฒ ์์ ์ ์ ๋ณด ์ ๋ฌ (๋ฉ์ธ์ง ์ ๋ฌ)
5) arin84 ๋ doyoon90 ์ด ์ ์ํด์ ๋ณด๋ธ ์์น์ ๋ณด๋ฅผ ๋ฐ๊ฒ ๋๋ค.
โ ํ ์คํธ 3 ) ์์ผ ์ข ๋ฃ ํ ์คํธ
6) doyoon90 ์ด ์ ์์ ์ข ๋ฃํ๊ณ ์๋ก์ด yeye91 ์ด ์ ์ํด๋ณด์. (์ด๋ฐ๊ฒฝ์ฐ ๋ฆฌ์คํธ์๋ arin84๋ง ์กด์ฌํด์ผํจ)
7) arin84 ๋ง ์์ผ์ ์กด์ฌํ๊ฒ๋๋ค.
8) ์ฆ ์ข ๋ฃ๋์ด์ yeye91 ์ด ๋ณด๋ด๋ ๋ฐ์ดํฐ๋ฅผ ๋ฐ์ ์ ์๊ฒ ๋๋ค. arin84 ๋ง ๊ณ์ํด์ ๋ฐ์