🔥 스파르타 TIL (프로젝트)

Slack API 적용해서 사용자에게 DM 보내는 기능을 만들어보자

승승장규 2025. 3. 18. 12:02

클라이언트에서 API 요청을 하면 해당 사용자에게 DM을 보내는 기능을 만들어보자.

향후에 MSA 통신으로 DM 보내는 기능으로 리팩터링 할 것 같다.

 

Slack API: Applications | Slack

Your Apps Don't see an app you're looking for? Sign in to another workspace.

api.slack.com

우선 Slack 기능을 사용하기 위해서 Token을 발급받아야 하는데, 위에 링크로 들어가서 Create New App을 통해 나만의 App을 만들어야 한다.

 

여기서 From scratch를 클릭한다.

그럼 App Name과 워크스페이스를 지정해줘야 하는데 만약 워크스페이스가 없다면 Slack에서 워크스페이스를 만들어야 한다.

이제 여기서 Settings에 보면 OAuth & Permissions를 클릭해 보면

 

Bot User OAuth Token을 발급받을 수 있다. 이제 DM을 보내기 위해 Scope를 설정해 보자.

밑으로 내리다 보면

Scopes를 설정할 수 있는데 여기선 원하는 Scope를 찾아서 사용하면 된다.

Add an OAuth Scope를 클릭해서 원하는 Scope를 찾아서 등록해 보자.

 

 

Permission scopes

The capabilities and permissions of Slack apps are governed by named scopes.

api.slack.com

 

 

channels:read = 공개 채널의 기본 정보를 조회할 수 있다.

chat:write = 봇이 메시지를 보낼 수 있다.

groups:write = 봇이 비공개 채널을 관리하고 새로 만들 수 있다.

im:write = 봇이 개인 메시지를 시작할 수 있다.

mpim:write = 봇이 그룹 메시지를 시작할 수 있다.

users:read = 워크스페이스의 사용자 정보를 조회할 수 있다.

users:read.email = 사용자 이메일 주소를 조회할 수 있다.

 

본인이 원하는 적절한 Scope 설정이 끝났다면 준비는 거의 끝난 거라고 볼 수 있다.

 

이제 메시지를 보낼 준비를 하면 되는데 어떤 메서드를 사용할지는 본인의 선택에 맞게 선택하면 된다.

 

Web API methods | Slack

 

api.slack.com

 

 

public class SlackService {

    private final SlackRepository slackRepository;

    private final RestTemplate restTemplate;

    public SlackService(SlackRepository slackRepository, RestTemplateBuilder builder) {
        this.slackRepository = slackRepository;
        restTemplate = builder.build();
    }

    @Value("${slack.token}")
    private String slackToken;
    
    @Transactional
    public SlackMessageResponse sendMessageToUser(SlackDto slackDto, String senderUsername) {
        try {
            String userId = getUserIdByName(slackDto.getUsername());

            if (userId == null) {
                throw new IllegalArgumentException("사용자를 찾을 수 없습니다");
            }
            String result = sendMessage(userId, slackDto.getMessage());

            JSONObject jsonObject = new JSONObject(result);
            if (!jsonObject.getBoolean("ok")) {
                throw new IllegalArgumentException("메시지 전송에 실패했습니다.");
            }
            
            Slack slack = Slack.create(
                    slackDto.getUsername(), slackDto.getMessage());
            slack.getCreatedBy(senderUsername);
            slackRepository.save(slack);

            return SlackMessageResponse.of(slack);
        } catch (Exception e) {
            throw new RuntimeException("메시지 전송 중 오류가 발생했습니다", e);
        }
    }
    
    public String getUserIdByName(String username) {
        JSONArray users = getAllSlackUsers();

        for (int i = 0; i < users.length(); i++) {
            JSONObject user = users.getJSONObject(i);
            if (user.has("profile") && user.getJSONObject("profile").has("real_name")) {
                String realName = user.getJSONObject("profile").getString("real_name");
                if (realName.equals(username)) {
                    return user.getString("id");
                }
            }
            if (user.has("profile") && user.getJSONObject("profile").has("display_name")) {
                String displayName = user.getJSONObject("profile").getString("display_name");
                if (displayName.equals(username)) {
                    return user.getString("id");
                }
            }
        }

        return null;
    }
    
    public JSONArray getAllSlackUsers() {
        String url = "https://slack.com/api/users.list";
        HttpHeaders headers = new HttpHeaders();
        headers.add("Authorization", "Bearer " + slackToken);
        HttpEntity<String> entity = new HttpEntity<>(headers);

        ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.GET, entity, String.class);

        JSONObject jsonResponse = new JSONObject(response.getBody());
        if (!jsonResponse.getBoolean("ok")) {
            throw new IllegalArgumentException("사용자를 불러오지 못했습니다.");
        }

        return jsonResponse.getJSONArray("members");
    }
    
    public String sendMessage(String userId, String message) {
        String openChannelUrl = "https://slack.com/api/conversations.open";
        HttpHeaders headers = new HttpHeaders();
        headers.add("Authorization", "Bearer " + slackToken);
        headers.add("Content-Type", "application/json");

        JSONObject jsonObject = new JSONObject();
        jsonObject.put("users", userId);

        HttpEntity<String> request = new HttpEntity<>(jsonObject.toString(), headers);
        String response = restTemplate.postForObject(openChannelUrl, request, String.class);

        JSONObject jsonResponse = new JSONObject(response);

        if (!jsonResponse.getBoolean("ok")) {
            throw new IllegalArgumentException("채널을 열 수 없습니다.");
        }

        String channelId = jsonResponse.getJSONObject("channel").getString("id");

        // 채널에 메시지 전송
        String sendMessageUrl = "https://slack.com/api/chat.postMessage";

        jsonObject = new JSONObject();
        jsonObject.put("channel", channelId);
        jsonObject.put("text", message);

        request = new HttpEntity<>(jsonObject.toString(), headers);
        return restTemplate.postForObject(sendMessageUrl, request, String.class);
    }
}

 

 

슬랙 사용자의 이름을 받아와서, 이름을 통해 해당 사용자의 Id를 조회한다. 만약 사용자가 존재한다면 해당 사용자에게 메시지를 보내는 기능을 구현했다. 슬랙 API 특성상 사용자 ID로 변환하는 과정이 반드시 필요하기 때문에 사용자 이름을 입력받고, Id로 변환하는 과정을 추가해 주었다.

 

 

 

슬랙 메시지가 잘 전달된 것을 볼 수 있다. 🖐️🖐️