Node.js Slack SDK를 이용해 슬랙 API를 연동하고, Heroku에 배포해보는 포스트입니다.

 

Node.js Slack SDK는 Slack의 API를 Node.js에서 간편하게 연동할 수 있도록 만들어진 모듈입니다. Web API, Events API, Interactive Message, RTM API, Incoming Webhooks를 제공합니다.

 


1. Slack 앱 만들기 및 권한 설정

https://api.slack.com/apps?new_app=1에 접속하여 슬랙 앱을 생성해 줍니다.

<슬랙 앱 생성>

 

App Name에 슬랙 앱의 이름을 입력하고 Development Slack Workspace는 개발 시 연동할 워크스페이스를 선택해 준 후 [Create App] 버튼을 눌러 앱을 생성해 줍니다.

 

앱을 생성해 준 후 왼쪽의 OAuth & Permissions 페이지로 이동합니다.

<앱 생성 후 OAuth & Permissions 페이지로 이동하기>

 

OAuth & Permissions 페이지에서 스크롤을 아래로 내리면 Scopes 섹션이 있고, 이 중 Bot Token Scopes에서 [Add an OAuth Scope]를 클릭해서 chat:write와 channels:history 스코프를 추가해 줍니다.

<Bot Token Scopes에 스코프 추가>

chat:write는 Web API로 메시지를 보내기 위한 스코프이고, channels:history는 Events API로 채널의 메시지를 이벤트로 받기 위한 스코프입니다. 이 두 스코프를 추가해 준 후 스크롤을 위로 올려 [Install App to Workspace]  버튼을 클릭하면 아래와 같이 앱을 설치할 때 필요한 권한을 확인하는 페이지가 나타납니다. [Allow]를 클릭해서 앱을 워크스페이스에 설치해 줍니다.

<앱 설치 시 권한 확인>

 

[Allow]  버튼을 클릭하면 OAuth & Permissions 설정 페이지로 돌아오는데, Bot User OAuth Access Token이 생성되어 있는 것을 확인할 수 있습니다. 이 토큰은 봇을 이용해 슬랙 API를 사용할 때 사용하게 될 토큰입니다.

 

 


2. Heroku 가입, CLI 설치, Node.js 환경 세팅

Heroku는 PaaS(Platform as a Service) 중 하나로써 애플리케이션 빌드, 배포, 운영 환경을 제공해줍니다. 뒤에서 Node.js로 개발한 슬랙봇 애플리케이션을 Heroku에 배포하여 Slack API 서버와 통신하도록 합니다.

 

https://www.heroku.com에 접속해서 회원 가입을 해준 후 https://devcenter.heroku.com/articles/getting-started-with-nodejs#set-up 을 참고해서 사용하는 운영체제에 맞게 Heroku CLI를 설치해주고 heroku login 명령을 실행해서 가입한 계정으로 로그인 해 줍니다.

(heroku는 git push 명령을 통해 서버에 코드를 배포하므로 git도 함께 설치되어 있어야 합니다.)

 

설치가 완료되면 heroku에서 제공하는 node.js 템플릿을 클론해 옵니다.

git clone https://github.com/heroku/node-js-getting-started.git hello-slack
cd hello-slack

 

3. Node.js Slack SDK 모듈 추가

클론해 온 폴더에는 기본적으로 Heroku에서 실행할 수 있도록 Node.js 기본 코드 및 파일들이 세팅되어 있습니다.

├── Procfile
├── README.md
├── app.json
├── index.js
├── package.json
├── public
│   ├── lang-logo.png
│   ├── node.svg
│   └── stylesheets
│       └── main.css
├── test.js
└── views
    ├── pages
    │   ├── db.ejs
    │   └── index.ejs
    └── partials
        ├── header.ejs
        └── nav.ejs

여기에 Slack API 연동을 위해 web-api 모듈을 설치해 줍니다.

# package.json에 정의된 dependency 설치
npm i

npm i @slack/web-api

 

index.js 파일을 열어보면 express 모듈을 이용해 작성된 서버 코드가 있습니다. 선호하는 웹 프레임워크를 사용해도 되고, 여기에서는 템플릿의 코드를 그대로 활용하고 라우팅만 추가해 주도록 하겠습니다. index.js 파일을 아래와 같이 수정해 줍니다.

const express = require('express')
const path = require('path')
const PORT = process.env.PORT || 5000

const { WebClient } = require('@slack/web-api');

// OAuth & Permissions 설정 페이지에서 생성된 Bot User OAuth Access Token
const token = 'xoxb으로 시작하는 BOT_USER_OAUTH_ACCESS_TOKEN 값으로 이 부분을 바꿔주세요.';
const web = new WebClient(token);

express()
  .use(express.static(path.join(__dirname, 'public')))
  .use(express.json())
  .set('views', path.join(__dirname, 'views'))
  .set('view engine', 'ejs')
  .get('/', (req, res) => res.render('pages/index'))
  .post('/slack/events', (req, res) => {
    let body = req.body;
    let event = body.event;

    if(body.type === 'event_callback') {
      if(event.type === 'message') {
        // 메시지 이벤트인 경우, 메시지가 '안녕'이면 '안녕하세요' 메시지 전송
        if(event.text === '안녕') {
          console.log(`인사 메시지 수신 channel:${event.channel}, user:${event.user}`);
          web.chat.postMessage({
            channel: event.channel,
            text: '안녕하세요.'
          }).then(result => {
            console.log('Message sent: ' + result.ts)
          });

          res.sendStatus(200);
        }
      }
    } else if(body.type === 'url_verification') {
      // URL 검증을 위한 처리
      console.log('url verification')
      res.send(body.challenge);
    } else {
      res.sendStatus(200);
    }
  })
  .listen(PORT, () => console.log(`Listening on ${ PORT }`))

 

코드 수정이 완료되면 아래의 명령을 실행하여 Heroku 앱을 생성하고 커밋, 푸시해줍니다.

heroku create # Heroku 앱 생성
# 배포된 앱에 접속할 수 있는 URL이 출력됩니다. 
# create 시 앱 이름을 지정해줄 수도 있습니다.
# Git Remote가 heroku라는 이름으로 추가됩니다.

git add .
git commit -m "슬랙 이벤트 연동 기능 추가"

git push heroku master # heroku origin으로 push하면 앱이 배포됩니다

 

Git push 명령을 통해 배포가 정상적으로 완료되면 아래와 같이 로그가 출력됩니다.

heroku create 명령을 실행했을 때도 출력 됐던 앱의 URL이 출력됩니다.

 


 

4. 슬랙 이벤트 구독 설정

브라우저의 슬랙 앱 페이지에서 OAuth & Permissions 아래의 Event Subscriptions 페이지로 이동합니다.

 

Enable Events 오른쪽의 스위치를 토글해서 On으로 바꿔준 후 Request URL 에는 heroku create 명령을 실행했을 때 출력된 URL을 붙여넣어주고 path는 express에서 설정해준 /slack/events 를 붙여줍니다.

제 경우에는 https://warm-sierra-40409.herokuapp.com/slack/events 를 설정해주었습니다.

 

올바른 URL을 입력했다면 Request URL 레이블 오른편에 Verified가 표시됩니다. 그리고 슬랙에서 발생하는 이벤트 중 사용자가 메시지를 포스트한 경우를 Node.js 앱으로 받기 위해 [Subscribe to events on behalf of users]에서 [Add Workspace Event]를 클릭해 준 후 message.channels를 찾아 등록해 줍니다.

<message.channels 이벤트 추가>

 

끝으로 페이지 하단의 [Save Changes]를 눌러 변경 사항을 저장해 줍니다. 앱의 permission scopes가 변경되었다고 앱을 재설치하라는 메시지가 나타납니다.

메시지의 reinstall your app 링크를 클릭해서 앱을 재설치 해줍니다.

 

 


 

5. 슬랙에서 확인

필요한 설정과 배포가 완료되어 슬랙과 슬랙봇 앱이 연동되어 있는 상태입니다. 이를 확인하기 위해 슬랙 앱이나 웹에서 연동한 워크스페이스로 접속한 후 채널에 앱을 추가하고 메시지를 보내보겠습니다.

<채널의 Add an app 클릭>

채널에 접속한 후 [Add an app]을 클릭해서 표시되는 앱 중 In your workspace에 표시되어 있는 실습으로 만든 앱을 추가해 줍니다.

채널에 <hello-slack> 앱 추가

 

해당 채널에서 '안녕' 메시지를 입력하면 봇이 '안녕하세요'라고 메시지를 보내는 것을 확인할 수 있습니다.

 


참고

- Slack Events API: https://api.slack.com/events-api 

- Slack Web API: https://api.slack.com/web

- Node.js Slack SDK: https://github.com/slackapi/node-slack-sdk

 

'밤을 지새다 > Javascript' 카테고리의 다른 글

node-schedule  (0) 2020.04.05

node-schedule은 Node.js에서 동작하는 스케줄링을 위한 모듈입니다. 원하는 시간에 작업을 수행할 수 있는 기능을 제공하고 유사한 모듈로는 node-cron이 있습니다. 자바스크립트의 setInterval 함수나 setTimeout 함수로도 동일한 기능을 구현할 수 있지만 그런 작업을 더 편하게 할 수 있도록 도와줍니다.

GitHub Repository: https://github.com/node-schedule/node-schedule

 

1. 모듈 추가

npm i node-schedule

 

2. Cron 스타일의 스케줄링
유닉스 계열의 Job 스케줄러인 cron의 표현식을 사용해서 스케줄링을 적용할 수 있습니다.

* * * * * *
│ │ │ │ │ │
│ │ │ │ │ └ 요일(0 - 7) (0 or 7 is Sun)
│ │ │ │ └───── 월 (1 - 12)
│ │ │ └────────── 일 (1 - 31)
│ │ └─────────────── 시 (0 - 23)
│ └──────────────────── 분 (0 - 59)
└───────────────────────── 초 (0 - 59, OPTIONAL)
const schedule = require('node-schedule');

// 매일 10시 정각에 '스크럼 시간입니다.' 출력
let job = schedule.scheduleJob('0 10 * * *', () => {
  console.log('스크럼 시간입니다.')
});

 

3. 날짜 기반 스케줄링
Date 객체를 전달해서 스케줄링 할 수 있습니다. Date 객체에 설정된 시간에 1회 작업을 수행합니다.

const schedule = require('node-schedule');

// 2020년 1월 1일 0시 0분 0초 - 월은 0부터 시작
const date = new Date(2020, 0, 1, 0, 0, 0);
let job = schedule.scheduleJob(date, () => {
console.log('새해가 밝았습니다.');
});

 

4. Recurrence Rule 스케줄링(반복 작업)
cron 표현식으로도 동일하게 반복 스케줄링 작업을 할 수 있지만 Recurrence Rule로 명시적으로 표현할 수 있습니다.

const schedule = require('node-schedule');

let recurrenceRule = new schedule.RecurrenceRule();
// Range 객체로 범위 설정
recurrenceRule.second = new schedule.Range(0, 59);
// 숫자 리터럴로 지정된 값 설정
recurrenceRule.minute = 20;

// 매시 20분 매초에 메시지 출력
schedule.scheduleJob(recurrenceRule, () => {
    console.log('Hello!')
});

 

5. 객체 리터럴
Recurrence Rule 대신 객체 리터럴로 전달할 수도 있습니다. 객체 리터럴로 전달 시 프로퍼티로는 second, minute, hour, date, month, year, dayOfWeek, start, end, rule이 있습니다.

const schedule = require('node-schedule');

// 매 0초 마다 'hi' 출력
schedule.scheduleJob({
  second: 0
}, () => {
  console.log('hi')
});
const schedule = require('node-schedule');

// startTime, endTime, rule 조합
let start = new Date(Date.now() + 5000); // 5초 뒤
let end = new Date(startTiime.getTime() + 5000); // 10초 뒤

// start ~ end 동안 매초 마다 'hi' 출력
schedule.scheduleJob({
  start,
  end,
  rule: '*/1 * * * * *'
}, () => {
  console.log('hi');
});

'밤을 지새다 > Javascript' 카테고리의 다른 글

Node.js로 슬랙 API 연동  (0) 2020.04.05

프로젝트에서 Spring Cloud OpenFeign을 사용하고 있는데, 연동하고 있는 서비스에서 종종 오류가 나는 경우가 있어 급한 대로 예외 처리만 해두었으나, 장기적으로 안정적인 장애 예방을 위해 Resilience4j를 적용하려고 문서와 예제들을 찾아보고 있다.

Netflix Hystrix를 먼저 생각해두고 있었으나, 2018년 11월부로 개발이 중단되고, maintenance mode라고 하여, Resilience4j를 사용하기로 결정. (Google Trend 상으로는 아직 Hystrix를 더 많이 찾고 있는 듯)

Google Trends - Hystrix vs Resilience4j (파랑이 Hystrix)

 

Resilience4j는 공식 문서에서 "fault tolerance library for Java™"라고 소개하고 있고 Netflix Hystrix에서 영감을 받아 만들었으며, Java 8, 함수형 프로그래밍에 맞게 설계되었다고 한다.

Resilience4j의 코어 모듈인 CircuitBreaker, Bulkhead, RateLimiter, Retry, TimeLimiter, Cache와 라이브러리나 프레임워크와 연동할 수 있는 애드 온 모듈들이 있다.

 

당장은 서킷 브레이커만 사용하면 돼서 resilience4j-spring-boot2 디펜던시를 추가한 후 아래와 같이 작성하고 동작할 지 테스트를 해보았다.

@FeignClient(name = "api")
interface ApiClient {
    @CircuitBreaker(name = "api", fallbackMethod = "fallback")
    @GetMapping("/users")
    fun getUsers(): List<User>
    
    // Exception을 받을 파라미터가 필요.
    // getUsers와 동일한 시그니쳐로 할 경우 NoSuchMethodException 발생
    fun fallback(e: Exception): List<User> = emptyList()
}

 

우선 위 코드는 오류와 함께 애플리케이션이 실행되지 않는다. @FeignClient가 적용되어 프록시를 생성할 때 fallback 메서드에 매핑 정보가 없기 때문이다. 애너테이션이 없으면 그냥 무시할 줄 알았는데, Feign은 인터페이스에 정의된 메서드에 대해 모두 처리한다. Retrofit은 다른가 싶어 테스트 해봤으나 Retrofit도 이에 대해서는 동일하다. @Ignore 따위의 애너테이션이 있을까 싶어 찾아봤지만 없었고, GitHub Issue에 사용자들이 올린 글을 찾다보니 Feign, Retrofit 모두 default 메서드에 대한 처리에 어려움이 있어 아직은 지원하지 않는 기능이라고 한다.

fallback 메서드에 @GetMapping("/ignore")와 같이 붙여주면 애플리케이션은 실행되지만 getUsers()를 호출하여 오류가 발생했을 때 폴백 메서드를 호출하긴 하지만 프록시된 fallback을 호출하기 때문에 결과적으로는 GET /ignore를 요청하게 되어 원하는대로의 폴백으로 동작하진 않는다.

 

resilience4j-feign도 있고, resilience4-spring-boot2도 있지만 Spring Cloud Open Feign에 resilience를 위의 코드처럼 작성하는 건 괜히 더 복잡해져서 돌아가더라도 쓸 수 있는 형태로 적용해 보기로 했다. 아래의 두 가지 방법으로 시도했다.

1. Spring Cloud Open Feign으로 만들어진 빈의 래퍼 컴포넌트 작성(resilience4j-spring-boot2)

2. Spring Cloud Open Feign을 사용하지 않고 resilience4j-feign을 통해 Feign 빈 생성

 


환경

  • IntelliJ IDEA
  • Kotlin 1.3.71
  • Gradle 6.3
  • Gradle Dependencies(Spring Intializr로 프로젝트 생성)
    • org.springframework.cloud:spring-cloud-starter-openfeign
    • io.github.resilience4j:resilience4j-feign:1.1.0 (1.3.x 버전이 있으나 내부에서 버전 충돌)
    • io.github.resilience4j:resilience4j-spring-boot2:1.1.0
    • org.springframework.boot:spring-boot-starter-webflux
    • org.springframework.boot:spring-boot-starter-aop
    • org.springframework.boot:spring-boot-starter-actuator

 

 

공통 구현

- Feign으로 호출할 테스트 컨트롤러 작성 - 404를 응답하는 핸들러 메서드를 작성

import org.springframework.http.HttpStatus
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.ResponseStatus
import org.springframework.web.bind.annotation.RestController
import reactor.core.publisher.Flux

@RestController
class TestApiController {
    @GetMapping("/api/test")
    @ResponseStatus(HttpStatus.NOT_FOUND)
    fun test(): Flux<String> = Flux.just("NOT_FOUND")
}

- @EnableFeignClients 추가

 


 

1. Spring Cloud Open Feign Wrapper 컴포넌트로 Resilience4j 적용

Feign 클라이언트 인터페이스 작성 - TestApiOpenFeignClient

import org.springframework.cloud.openfeign.FeignClient
import org.springframework.web.bind.annotation.GetMapping

@FeignClient(name = "test", url = "http://localhost:8080/api")
interface TestApiOpenFeignClient {
    @GetMapping("/test")
    fun getTest(): String
}

 

Wrapper 작성 - TestApiOpenFeignClientWrapper

import io.github.resilience4j.circuitbreaker.annotation.CircuitBreaker
import org.springframework.stereotype.Component

@Component
class TestApiOpenFeignClientWrapper(
        private val testApiOpenFeignClient: TestApiOpenFeignClient
) {
    @CircuitBreaker(name = "OPEN_FEIGN_API", fallbackMethod = "fallback")
    fun getTest(): String {
        return testApiOpenFeignClient.getTest();
    }

    /**
     * 서킷 브레이커에서 호출할 Fallback 메서드
     * @param e 발생한 예외
     */
    fun fallback(e: Exception): String {
        return "RESULT BY FALLBACK"
    }
}

 

테스트

import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest

@SpringBootTest
internal class TestApiOpenFeignClientWrapperTest {
    @Autowired
    private lateinit var client: TestApiOpenFeignClientWrapper

    @Test
    fun getTest() {
        assertEquals("RESULT BY FALLBACK", client.getTest())
    }
}

 


 

2. Resilience4j-feign으로 Feign 인스턴스 생성

Feign 클라이언트 인터페이스 작성 - TestApiFeignClient

import feign.RequestLine

interface TestApiFeignClient {
    @RequestLine("GET /api/test")
    fun getTest(): String
}

 

Feign 빈 정의 - FeignConfiguration

import feign.FeignException
import io.github.resilience4j.circuitbreaker.CircuitBreakerRegistry
import io.github.resilience4j.feign.FeignDecorators
import io.github.resilience4j.feign.Resilience4jFeign
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration

@Configuration
class FeignConfiguration {
    @Bean
    fun testApiClient(registry: CircuitBreakerRegistry): TestApiFeignClient {
        val circuitBreaker = registry.circuitBreaker("FEIGN_API")

        val decorators = FeignDecorators.builder()
                .withCircuitBreaker(circuitBreaker)
                .withFallback(object: TestApiFeignClient {
                    override fun getTest() = "RESULT BY FALLBACK"
                }, FeignException::class.java)
                .build()

        return Resilience4jFeign.builder(decorators)
                .target(TestApiFeignClient::class.java, "http://localhost:8080/api")
    }
}

 

테스트

import feign.RequestLine

interface TestApiFeignClient {
    @RequestLine("GET /api/test")
    fun getTest(): String
}

- hibernate 버전: 5.0.12-FINAL


스프링 배치 잡 작업 중 뜬금없이 하이버네이트의 EntityEntryContext 클래스의 reentrantSafeEntityEntries() 메서드에서 ArrayIndexOutOfBoundsException이 발생했다.


java.lang.ArrayIndexOutOfBoundsException: 6673

at org.hibernate.engine.internal.EntityEntryContext.reentrantSafeEntityEntries(EntityEntryContext.java:319)

at org.hibernate.engine.internal.StatefulPersistenceContext.reentrantSafeEntityEntries(StatefulPersistenceContext.java:1128)

at org.hibernate.engine.internal.AbstractFlushingEventListener.prepareEntityFlushes(AbstractFlushingEventListener.java:136)

...


검색하다가 hibernate 커뮤니티의 JIRA에서 동일한 이슈[각주:1]를 발견할 수 있었는데 해당 이슈 보고자도 멀티스레드 환경에서 엔티티를 저장하는 과정에서 가끔 발생했다고 한다.

커멘트의 마지막 부분에 관련 이슈[각주:2]와 함께 이슈를 클로즈한다고 하여 따라가보니 5.1 버전에 픽스되었다고 하여 일단은 버전업을 해보는 것으로 해결.

  1. https://hibernate.atlassian.net/browse/HHH-8880 [본문으로]
  2. https://hibernate.atlassian.net/browse/HHH-10795 [본문으로]

혼자서 어설픈 영어 실력으로 169페이지에 달하는 RFC 문서를 얼마나 번역할 수 있을지는 의문이지만(...) iCalendar의 개념과 프로젝트에서 사용할 Event 타입에 대해 파악하고자 한글로 정리해봅니다.



1. 소개

지난 10년간 캘린더와 일정의 사용이 상당히 증가했습니다. 엔터프라이즈 및 엔터프라이즈간 비즈니스는 이 정보 기술을 사용하여 이벤트 및 작업을 신속하게 스케줄링하는데 의존하게 되었습니다. 이 RFC는 서로 다른 일정 관리 애플리케이션과 일정 관리 애플리케이션 간에 가능한 상호 운용성 수준을 향상시키기 위한 것입니다. 이 RFC 문서는 전자 캘린더 및 일정 정보를 교환하기 위한 MIME 콘텐츠 유형을 정의합니다. 인터넷 일정 및 예약 핵심 객체 사양 또는 iCalendar를 사용하면 일정 및 일정 관리 애플리케이션에 일반적으로 저장된 정보를 캡처하고 교환할 수 있습니다. PIM(개인 정보 관리자, Personal Information Manager) 또는 그룹 일정 관리 제품과 같은 다른 시스템에서 사용할 수 있습니다.


iCalendar 포맷은 애플리케이션이나 시스템간의 교환 형식으로 적합합니다. 형식은 MIME 컨텐츠 타입으로 정의됩니다. 이렇게 하면 SMTP, HTTP, 파일시스템, 메모리 기반 클립보드 사용 또는 드래그/드랍, 점대점 비동기 통신과 같은 데스크탑 대화형 프로토콜을 비롯한 여러 전송 매체를 사용하여 개체를 교환할 수 있습니다. 통신, 유선 네트워크 전송 또는 적외선과 같은 유선 전송의 일부 형태로 제공됩니다.


이 RFC 문서는 모임이나 약속, 할 일, 업무 일지 항목 요청, 회신, 수정 및 취소와 같은 일정 및 일적 작업을 지원하기 위해 이 컨텐츠 타입을 메시지 집합에 매핑하는 iCalendar 개체 메서드의 정의도 제공합니다. iCalendar 개체 메서드를 사용하여 약속 있음 / 없음 시간 데이터 요청 및 응답과 같은 다른 일정 및 예약 작업을 정의할 수 있습니다. 이러한 스케줄링 프로토콜은 [2446bis]에 정의된 iCalendar iTIP(iCalendar Transport-Independent Interoperability) 에서 정의됩니다.


이 RFC 문서에는 [RFC5234]에 정의된 인터넷 ABNF를 기반으로 하는 컨텐트 타입에 대한 공식 문법도 포함됩니다. 이 ABNF는 파서를 구현 시 필요하며 메모의 서술적인 구문 정의를 해석할 때 모호함이나 질문이 생길 때 최종 참고 자료로 사용됩니다. ABNF 구문으로 쉽게 표현할 수 없는 추가 제한 사항은 ABNF에서 주석으로 지정됩니다. 표준 구문에 대한 주석은 그렇게 취급됩니다.



2. 기본 문법과 컨벤션

이 문서에서 "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY" 그리고 "OPTIONAL" 키워드는 [RFC2119]에서 설명하고 있는대로 해석되어야 합니다.


이 RFC 문서는 캘린더 및 일정 포맷을 정의하기 위해 설명적인 산문과 공식적인 표기법 모두 사용합니다.


이 RFC 문서에 사용된 모든 숫자 값은 십진수 표기법으로 제공됩니다.


모든 속성 이름, 속성 매개변수, 열거 속성 값 및 속성 매개 변수 값은 대/소문자를 구분하지 않습니다. 그러나 달리 명시하지 않는 한 다른 모든 속성 값은 대/소문자를 구분합니다.


참고: 모든 들여쓰기 된 편집 노트는 독자에게 추가적인 정보를 제공하기 위한 것입니다. 이 정보는 이 RFC를 준수하는 구현을 구축하는데 필수적이지 않습니다. 이 정보는 RFC의 특정 특징이나 특성을 강조하기 위해 제공됩니다.


 iCalendar 객체의 형식은 [RFC2425] text/directory 미디어 타입의 구문을 기반으로 합니다. iCalendar 객체는 text/directory 미디어 타입 [RFC2425]의 프로파일은 아니지만 [RFC2425] 명세의 여러 요소를 재사용합니다.




2.1. 포맷팅 컨벤션

이 문서에 정의된 요소는 설명에 정의되어 있습니다. 이들을 설명하는데 사용된 많은 용오는 이 문서의 표준 사용법과 다른 일반적인 사용법을 가집니다. 이 문서에서 일정 및 일정 모델, 핵심 개체(Core object) 또는 상호 운용성 프로토콜 [2446bis]의 요소를 참조하기 위해 일부 포맷팅 컨벤션이 사용되었습니다. 캘린더 및 스케줄 롤은 각 단어의 첫문자가 대문자인 텍스트의 인용 문자열로 참조됩니다. 예를 들어 "Organizer"는 [2446bis]에 의해 정의된 스케줄링 프로토콜 내에서 "Calendar User"의 롤을 나타냅니다. 이 문서에 정의된 캘린더 구성 요소는 대문자로 된 따옴표로 묶은 텍스트 문자열로 참조됩니다. 모든 캘린더 구성 요소는 문자 "V"로 시작합니다. 예를 들어, "VEVENT"는 이벤트 일정 구성 요소를 나타내고 "VTODO"는 수행할 일정(TO DO) 구성 요소를 나타내고 "VJOURNAL"은 일일 업무 캘린더 구성요소를 나타냅니다. iTIP [2446bis]에 의해 정의된 스케줄링 방법은 대문자로 된 따옴표로 감싸진 텍스트 문자열로 참조됩니다. 예를 들어 "REQUEST"는 스케줄링 캘린더 구성 요소를 생성 또는 수정하도록 요청하는 방법을 나타내며 "REPLY"는 요청 수신자가 일정 구성 요소의 "Organizer"로 상태를 업데이트 하는데 사용하는 방법을 나타냅니다.


이 문서에 정의된 속성은 대문자로 된 따옴표로 감싼 텍스트 문자열과 "property"라는 단어로 참조됩니다. 예를 들어, "ATTENDEE" 속성은 캘린더 사용자의 주소를 전달하는데 사용되는 iCalendar 속성을 참조합니다. 이 문서에 정의된 속성 파라미터는 소문자로 묶인 텍스트 문자열과 "parameter"라는 단어로 참조됩니다. 예를 들어, "value"  특성 값의 기본값 유형을 대체하는데 사용되는 iCalendar 특성 파라미터를 나타냅니다. 이 노트에 정의된 열거형 값은 대문자로 된 텍스트를 단독으로 사용하거나, "value"라는 단어를 사용하여 참조됩니다. 예를 들어 "MINUTELY" 값은 "RECUR" 값 유형의 "FREQ" 구성 요소와 함께 사용되어 1분 이상의 간격을 기반으로 반복 구성 요소를 지정합니다.


다음 표에는 이 문서에서 참조하는 [US-ASCII] 문자 집합의 다른 문자가 나열되어 있습니다. 각 문제에 대해 이 표는 US-ASCII 10 진수 코드포인트와 함께 이 문서에서 사용된 문자 이름을 지정합니다.


+------------------------+-------------------+

| Character name         | Decimal codepoint |

+------------------------+-------------------+

| HTAB                   | 9                 |

| LF                     | 10                |

| CR                     | 13                |

| DQUOTE                 | 22                |

| SPACE                  | 32                |

| PLUS SIGN              | 43                |

| COMMA                  | 44                |

| HYPHEN-MINUS           | 45                |

| PERIOD                 | 46                |

| SOLIDUS                | 47                |

| COLON                  | 58                |

| SEMICOLON              | 59                |

| LATIN CAPITAL LETTER N | 78                |

| LATIN CAPITAL LETTER T | 84                |

| LATIN CAPITAL LETTER X | 88                |

| LATIN CAPITAL LETTER Z | 90                |

| BACKSLASH              | 92                |

| LATIN SMALL LETTER N   | 110               |

+------------------------+-------------------+ 



2.2. 관련 메모

이 RFC를 구현하는 사람은 이 문서와 함께 인터넷 캘린더 및 스케줄링 표준을 위한 프레임워크는 구성하는 몇 가지 다른 문서에 대해 잘 알고 있어야 합니다. 이 문서는 객체, 값의 타입, 속성과 속성 매개변수의 핵심 명세를 지정합니다.


- iTIP [2446bis]는 서로 다른 구현 간의 스케줄링을 위한 상호 운용성 프로토콜을 지정합니다.

- iCalendar 메시지 기반 상호 운용성 프로토콜(iMIP) [2447bis]는 [2446bis]에 대한 인터넷 전자 메일 바인딩을 지정합니다.


이 문서는 다른 문서에서 개념이나 정의의 명세를 반복하려고 시도하지 않습니다. 가능한 경우 이러한 개념이나 정의의 명세를 제공하는 문서를 참조하십시오.


3. iCalendar 객체 명세


다음 섹션에서는 캘린더 및 스케줄링 코어 객체 명세에 대한 세부 정보를 정의합니다. Calendaring, Scheduling Core Object 는 캘린더링과 스케줄링 정보의 컬렉션입니다. 일반적으로 이 정보는 하나 이상의 iCalendar 객체가 있는 iCalendar 스트림으로 구성됩니다. iCalendar 객체의 본문은 일련의 캘린더 속성과 하나 이상의 캘린더 구성 요소로 구성됩니다.


3.1. 절에서는 content 라인의 포맷을 정의합니다. 

3.2. 절에서는 속성 파라미터 형식을 정의합니다.

3.3. 절에서는 속성 값의 데이터 타입을 정의합니다.

3.4. 절에서는 iCalendar 객체 포맷을 정의합니다.

3.5. 절에서는 iCalendar 속성 타입을 정의합니다.

3.6. 절에서는 캘린더 구성 요소 타입을 정의합니다.

3.7. 절에서는 캘린더 속성을 정의합니다.

3.8. 절에서는 캘린더 구성 요소 등록 정보를 정의합니다.


이 정보는 MIME 컨텐츠 타입 등록을 위한 필수 요소입니다. 또한 이 정보는 그러한 컨텐츠 등록과 독립적으로 사용될 수 있습니다. 특히 이 문서는 파일, 메모리 또는 네트워크 기반 전송 메커니즘에서 캘린더 및 스케줄 교환 포맷을 위해 직접 적용할 수 있습니다.





Java 5부터 등장한 제너릭을 이제서야 정리해보려고 한다. 사실 만들어져 있는 제너릭 API들은 많이 사용해왔지만 직접 제너릭 클래스를 작성하는 일은 아무래도 일반 클래스를 작성하는 것보다 빈도가 낮다보니, 제너릭에 관해 최근에서야 알게 된 것들이 있기도 하다.

나는 이미 자바의 버전이 6일 때 배우기 시작해서 제너릭 전과 후의 차이를 극적으로 느끼지는 못했지만 가끔 레거시 코드 중에 제너릭을 쓰지 않고 컬렉션을 사용하는 코드를 보면 조금 힘들긴 하다(물론 제너릭이 늘 옳은 것만은 아니라고 생각한다).

1. 제너릭을 왜 쓰는 걸까요?

2. 제너릭 타입

3. Parameterized Type

4. 제너릭 메서드

5. Bounded Type Parameter

- Class & Interfaces.

6. 제너릭 타입 추론

7. 제너릭으론 할 수 없는 것들

8. 그렇게 중요하진 않지만 흥미로운 사실 - Multiple Extended(Implemented) Generic Interface.

9. assignable - Foo<T>, Bar extends Foo<T>, Foo assignable Bar. @Autowired

10. 제너릭 리플렉션

면접 질문 중에 가장 당황했던 질문이 아니었나 싶다.


"StackOverflow가 발생했을 때 어떻게 해결하실건가요?"


너무 OutOfMemoryError에 대해서만 대비를 했던 탓인지 카운터를 맞았다. 답변도 OutOfMemoryError에 대한 해결 방법으로 해버렸다. 그러자 면접관께서 "그건 Heap 메모리에서구요, StackOverflow는 어떻게 될까요?"하고 내가 잘못 이해했다고 생각을 하셨는지 다시금 바로 잡아주셨지만.. 머리가 하얘졌더랬다.


면접이 끝나고, 정신이 좀 돌아오니 그제야 StackOverflowError는 호출 스택의 깊이가 너무 깊어질 때 발생한다는 게 생각났다. 웬만해선 질문을 받아도 알면 대답하고, 모르면 모른다고 대답을 했을텐데, 말그대로 정말 당황해서 어버버 했었던 것 같다.


먼저 Java API 문서의 StackOverflowError 클래스의 설명에는 "Thrown when a stack overflow occurs because an application recurses too deeply"로 되어있다.  그럼 StackOverflowError가 발생했을 때 어떻게 해야할까? Heap 영역과 마찬가지로 Stack 영역의 메모리를 늘려주면 되는걸까? 복구는 가능할까?


일단 에러 상황을 만들기 위해 무한 재귀호출을 하도록 아래와 같이 작성했다.



그리고 JVM Option에서 -Xss10K를 추가해서 실행해보았다. 근데 아래와 같은 메시지를 출력하면서 실행되지 않는다.


Error: Could not create the Java Virtual Machine.

Error: A fatal exception has occurred. Program will exit.


The stack size specified is too small, Specify at least 160k


160K의 기준이 있나 싶어 1.8 버전의 JVM 명세를 찾아봤으나 딱히 160K에 대한 기준은 없은 것으로 봐서는 JVM 구현에서 결정하는 사항인 듯 하다. 검색을 하다보니 CASSANDRA 이슈에서 Stack 사이즈와 관련된 내용이 있었는데 Java 6의 경우는 -Xss128K도 되는데 Java 7부터는 160K의 하한이 생긴 모양이다. (https://issues.apache.org/jira/browse/CASSANDRA-4275)


다시 스택 크기를 일단 최소로 잡아서 160K로 조정해서 실행해보았다. 751까지 출력된 후 StackOverflowError를 발생시킨다. 일부러 재귀구조로 프로그램을 작성하지 않는 이상 이 정도의 깊이까지 호출하는 경우가 있을지는 의문이긴하다.


스택 크기를 320K로 조정했을 때는 2615까지, 480K일때는 4476, 640K일 때는 6345로 출력되었다. 메서드 구현에 따라 스택 메모리를 소모하는 정도는 달라질 수 있을 것이고, 또한 스택 크기를 지정할 때 스레드 스택 프레임을 위해 기본으로 할당되는 영역까지 고려되어야 할 것 같다. Stack 메모리는 스레드별로 할당되므로 요청을 스레드별로 처리하는 웹 애플리케이션의 경우에는 Heap과 Stack의 크기가 세심하게 설정되어야 할 것 같지만 아직은 경험이 없으니 이에 대한 방법은 더 공부를 해봐야겠다.



그럼 StackOverflowError가 발생했을 때 일반적인 Exception과 마찬가지로 예외 처리가 가능할까? 일단 Error도 try-catch로 Error를 잡을 수는 있지만, 이미 비정상적인 조건에서 발생하게 되므로, 에러가 발생하기 전까지, 혹은 그 후에 처리가 정상적으로 되었는지 확신하기 어렵다. 이 경우에는 Error를 잡는 것보다, Error가 발생하는 원인을 찾아 해결하는 쪽이 더 바람직하다고 본다.



※ 참고 자료

- https://docs.oracle.com/javase/7/docs/api/java/lang/StackOverflowError.html

- https://docs.oracle.com/javase/7/docs/api/java/lang/Error.html

- https://docs.oracle.com/javase/specs/jvms/se8/jvms8.pdf

https://issues.apache.org/jira/browse/CASSANDRA-4275

- http://stackoverflow.com/questions/20658264/when-does-stackoverflowerror-occur

   웹(클라우드) IDE은 웹 환경, 즉 브라우저를 통해 인터페이스를 제공하는 통합 개발 환경입니다. 

로컬 PC에 IDE를 다운로드 받아 실행하지 않고 웹 브라우저를 통해 접속하여 해당 서비스에서 제공하는 환경을 통해 개발할 수 있습니다.


   지원하는 언어, 미들웨어, 데이터베이스 등의 종류와 기능은 각 서비스 별로 상이하지만 파일 브라우징, 버전 관리 연동, 편집기, 콘솔, 터미널 등의 기본적인 기능은 대동소이합니다.


   이 글에서는 상용 서비스와 오픈 소스로 공개된 웹 IDE에 대해 비교해보도록 하겠습니다. 이전에 웹 IDE를 분석했던 자료를 PT로 정리해뒀었는데, 묻어두긴 아까워 PT자료를 토대로 글로 옮겼습니다. 슬라이드 그대로 짜깁기한 부분도 있으니 너그러이 양해를 :D


※ PT 자료는 http://www.slideshare.net/ssusercef361/ide-60399120 에서 보시거나 다운로드 받으실 수 있습니다.




I. 주요 상용 웹 IDE



   이 중 NITROUS는 회원 가입 시 전화번호 인증이 필요한데 한국 번호(+82)로는 인증 문자가 발송되지 않는 건지 인증을 하지 못해서 테스트해보지 못했습니다 ;;




  상용 웹 IDE의 공통 기능


대부분의 서비스가 아래와 같은 기능을 제공합니다.


1. 프로젝트(파일) 관리/브라우징

   - 워크스페이스 제공

   - 언어/프레임워크별 프로젝트 템플릿 제공

   - 파일 업로드/다운로드

   - 프로젝트 내보내기/가져오기 (파일 또는 링크 형태의 공유)


2. 편집기(문법 검사, 코드 하이라이팅, 자동 완성 등)


3. 자동 저장 및 리비전(SCM과는 별도로 파일 자체의 리비전 관리)


4. 개발, 빌드 및 실행을 위한 VM이나 Container 제공


5. 협업 기능

   - 프로젝트 공유/프로젝트 멤버(팀) 관리

   - IDE 내 메신저 채널 제공

   - 동시 협업 코딩(구글 문서와 같은 동시 편집 기능)


6. 웹 기반 SSH 터미널 제공(대부분 sudo 명령어 지원)


7. 형상관리 시스템 연동(Git.Mercurial/SVN 또는 외부 서비스 e.g. Github)


8. UI 사용자화(Custumizing)

   - 창/탭 형태의 UX 제공

   - 창/탭 분할

   - 테마 사용자화



   개발/빌드/실행 환경에 있어서는 VM이나 Container 기반의 워크스페이스(또는 프로젝트)를 제공하며, 서비스에 따라서 실행 및 빌드 시에만 VM(Container)를 생성하는 경우도 있습니다. 또한 자체적으로 웹 애플리케이션 테스트를 할 수 있는 경우 Public URL을 제공하여 해당 URL을 통해 실행된 웹 애플리케이션을 확인할 수 있습니다.

   대부분의 경우 웹 IDE에서는 테스트 환경까지는 제공하지만 배포환경은 제공하지 않습니다. 즉 웹 IDE를 통해서 안정된 서비스를 운영하기는 아직까지 어려워보입니다.

웹 IDE에 따라서는 Cloud 서비스(IaaS/PaaS)와 연동하여 개발된 결과를 해당 서비스에 배포할 수 있도록 하는 기능도 제공합니다.





  Codenvy - https://codenvy.com/

 

Codenvy의 주요 특징


   - Docker 기반 컨테이너 제공

   - 프로젝트 실행 시에만 컨테이너(Runner) 동작

      - Runner가 동작중일 때 웹 SSH Terminal을 통해 접속가능

   - 웹 UI를 통한 데이터베이스 연동

   - 언어 지원 : C/C++, Javascript, Java, PHP, Python, Ruby

   - 템플릿 프로젝트 제공

   - 언어별 빌드툴에 대한 빌더 인터페이스 제공

   - 형상관리 제공(Git, Subversion)


   Codenvy를 통해 개발 시 소스코드 등의 프로젝트 파일은 서버의 파일 시스템으로만 관리되고, 테스트 시에는 Runner라는 실행환경을 제공합니다. Runner는 Docker를 통해 생성된 Container이며, 빌드 결과를 Runner에 업로드하여 Runner 환경 내에서 실행됩니다.

    Runner는 언어/프레임워크/웹서버/WAS 등에 따라 다양하게 제공되고 있으며 2015년 11월 기준으로 제공되는 Runner는 아래와 같습니다.


   - Android 4.2.2 + VNC + Java 7

   - Android 4.3.1 + VNC + Java 7

   - Android 4.4.2 + VNC + Java 7

   - Apache 2

   - Apache 2.4 + MySQL 14.14 + PHP 5.6

   - C++

   - Cassandra DB 2.0 + Java 7

   - Codenvy CLI  + Java 7

   - Couchbase 3.0.1 + Java 7

   - Django + Python 2.7

   - GlassFish 4.0 + Java 7

   - Go Console 1.3

   - Go Web 1.3

   - Google App Engine SDK 1.9.14 + Java 7

   - Google App Engine SDK 1.9.14 + PHP 5.6

   - Google App Engine SDK 1.9.14 + Python 2.7

   - RiakDB 1.4 + Java 7

   - Ruby 2.1

   - Tomcat 7.0 + Java 7

   - TomEE 1.5 + Java 7

   - Google App Engine SDK 1.9.19+ Java 7

   - Google App Engine SDK 1.9.19 + PHP 5.6

   - Google App Engine SDK 1.9.19 + Python 2.7

   - Google App Engine SDK 1.9.14, Python 2.7

   - Grun 0.4 + Node.js 0.10 + Angular JS 1.2

   - Gulp 3.8 + Node.js 0.10 + Angular JS 1.2

   - Java 7

   - JBoss 7.1 + Java 7

   - Jetty 9.2 + Java 7

   - MongoDB 2.6 + Java 7

   - MySQL 5.5 + Java 7

   - Neo4j 2.1 + Java 7

   - NuoDB 2.0 + Java 7

   - Play 1.2 + Java 7

   - PostgreSQL 9.3 + Java 7

   - Python 2.7

   - Python 3.4

   - Qt4 + C++

   - Rails 4.0 + Ruby 2.1

   - Virgo 3.6 + Java 7

   - VNC + Java 7



   또한 기본적으로 제공되는 Runner이외에도 현재의 Runner를 통해 Custom Runner를 구성하여 생성할 수 있습니다.


Codenvy 아키텍쳐




주요 화면 캡처

(전체 화면 스크린샷이라 잘 보이지 않습니다 ;; 자세한 화면은 슬라이드쉐어에 공유한 PT 자료를 참고해주세요)




기본 편집 화면





새 프로젝트 생성 - 언어별 템플릿 프로젝트 제공





러너 및 러너 속성 





러너 실행 시 콘솔을 통한 로그 확인





러너 접속을 위한 터미널





데이터베이스 연결 - 

데이터베이스(PostgreSQL, MySQL, Oracle, MS SQL, NuoDB) 가 설치된 URL을 직접 입력하거나

Google Cloud SQL, Amazon의 데이터베이스에 연결






  SourceLair

 

SourceLair의 주요 특징


   - 형상관리 시스템에 밀접하게 연동

      - 프로젝트 생성 시 형상관리 시스템을 먼저 선택하도록 되어있음

   - UI 상에서 Package Management 제공

      - npm, pip, bower 등

   - 개발용 데이터베이스 인스턴스 제공

   - Heroku 개발 특화

      - Heroku 앱 템플릿 제공(Heroku 용 Node, Django)

   - Linux 터미널 제공

   - 웹 서비스의 Public URL 제공

   - Sublime Text의 Command Palette와 유사한 기능 제공

   - HTML 라이브 프리뷰


   편집 기능에 있어서 강력한 기능을 많이 제공합니다. HTML 의 라이브 프리뷰도 그렇고, 자동 완성, 코드 폴딩 등의 기능을 제공합니다. 웹에서 웬만한 IDE의 편집기에 버금가는 기능을 갖췄다니 대단하네요.

   특이한 부분은 웹 UI 내에서 npm, pip와 같은 패키지 관리를 할 수 있단 부분인데요. 물론 다른 웹 IDE 들도 터미널을 통해 할 수 있지만 UI를 통해서 할 수 있는 건 SourceLair 밖에 없어 보입니다.



편집 화면 캡처



편집 화면 - 상단 메뉴 대신 왼쪽에 바 형태로 아이콘을 제공합니다. 편집기에서는 탭으로 파일을 구분하여 작업할 수 있습니다.






  Koding - http://www.koding.io

 

Koding 의 주요 특징


   - VM 기반 워크스페이스 제공 : VM에 대한 Public IP 제공

   - 편집기 위주의 화면 구성

   - 빌드, 테스트 등은 터미널을 통해 명령어/스크립트로 수행

   - 그림판(Drawing Board) 편집기 제공

   - 메신저(개인 Direct Message / 채널)

      - 채널의 경우 공개되어 있으며, 다른 사용자들과 의견 공유 가능

   - VM 관리

      - VM Spec, Disk Usage, Domain, VM Share, Snapshot 등 IaaS 주요 VM 관리 기능 제공

      - 외부 서비스의 VM 사용 가능 - 해당 VM에서 셋업 스크립트 실행을 통해 초기화


Koding의 경우 다른 서비스들과 달리 Koding을 위한 개발환경이 세팅된 VM을 통째로 제공해 줍니다. VM안의 파일 시스템을 사용하고 언어, 프레임워크, 데이터베이스 등을 설치하여 사용할 수 있습니다. 가장 자유도가 높은 웹 IDE 입니다.


또한 사용하는 IaaS가 있다면 그곳의 VM에 셋업 스크립트를 실행해서 Koding 에서 사용할 수 있도록 설정할 수 있습니다.



주요화면 스크린샷



편집 화면 - Drawing Board에 Hello World 써보기 (악필...)





VM 설정 화면





채널을 통한 커뮤니케이션





Direct Message





외부 VM을 추가하기





  codeanywhere - http://www..io

 

codeanywhere 의 주요 특징


   - 컨테이너, FTP/SFTP/SSH, Git, 스토리지 서비스로부터 프로젝트를 생성/파일 관리

      - 자체 제공 컨테이너 : 언어 및 프레임워크별 이미지 제공

      - Github/Bitbucket 연동 또는 Git URL 직접 입력

      - 3rd 파티 스토리지 서비스 : 아마존 S3, Dropbox. Google Drive

   - 프로젝트 템플릿은 제공하지 않음

   - 컨테이너의 언어(프레임워크) 스택에 따라 웹 서버 실행

   - 스택에 필요한 패키지 설치 후 커스텀 스택으로 저장 할 수 있음

   - 다른 사용자에게 프로젝트 공유 기능 지원

   - 프로젝트/컨테이너 등의 설정은 JSON 형식의 설정 파일을 직접 수정


   Codeanywhere은 프로젝트 생성 시 '연결'한다는 개념으로 접근합니다. 컨테이너에 연결, GitHub에 연결, SSH 서버에 연결.. 등 파일 시스템을 사용할 수 있는 시스템을 기반으로 하여 개발을 할 수 있도록 해두었습니다. 다만 컨테이너 이 외에는 통합 개발 환경을 제공하기 보다는 웹을 통한 파일 관리의 느낌이 더 드는..?

   컨테이너의 경우에는 '스택'이라는 개념으로 언어나 프레임워크 등을 관리할 수 있도록 되어있고, 개발자가 원하는 패키지를 설치한 후 커스텀 스택으로 저장하여 사용할 수 있습니다.



주요 화면 캡처


프로젝트 생성 (Connection Wizard)






컨테이너 연결 - OS 선택(Ubuntu / CentOS)





편집 화면 - 기본적인 IDE와 비슷한 구성(터미널 제공)







  Cloud9 - https://c9.io

 

Cloud9 의 주요 특징


   - 컨테이너 기반 개발 환경 제공

   - REPL(Javascript) 제공

   - WAS의 로그 콘솔 제공

   - 로컬 IDE와 유사한 수준의 디버깅 기능 제공

   - 컨테이너 리소스 모니터링 및 프로세스 관리 가능

   - HTML 페이지, 마크다운, 이미지 파일 등의 프리뷰 지원


Cloud9은 개인적으로 이름이 마음에 드는 서비스입니다. 이름이 갖는 의미 만큼의 개발 환경을 제공해 줄 수 있을지는 더 지켜봐야겠지만요..? (오래오래...?)

SourceLair의 경우에는 웹 UI 상에서 패키지 관리를 제공했다면, Cloud9은 컨테이너의 메모리, 디스크와 같은 리소스와 컨테이너에서 실행 중인 프로세스를 관리할 수 있는 기능을 제공합니다.

또 특이하게 REPL(Read Eval Print Loop, 인터프리터 언어의 대화형 쉘)을 제공하는데, 웹 브라우저의 개발자 도구를 활용해도 되긴 하지만 바로 사용할 수 있다는 점에서 편리하긴 하네요. 또한 높은 수준의 디버깅 기능을 제공합니다.



주요 화면 캡처



로그인 후 프로젝트 메인 화면, 사용률 및 README 파일 확인 가능





편집 화면 - 설정 파일 편집(JSON 형식), 오른쪽에는 디버거





프리뷰(Preview) 를 통한 확인





Rails의 출력 로그 확인(터미널과 별도로 제공)






  구름IDE - https://www.goorm.io

 

구름IDE의 주요 특징


   - 국내에서 개발된 웹 IDE

   - 컨테이너(Amazon EC2 Container) 기반 워크스페이스 제공

      - *.goorm.io / *.compute.amazonaws.com Public URL 제공

   - Java 교육용 컨텐츠(예제 및 템플릿) 제공

   - 로컬 IDE와 유사한 수준의 디버깅 기능 제공

   - Git/Subversion 연동 (다소 불안정)

   - 문서 뷰어 제공(Slideshare.net, PDF 파일)

   - 작업 내역(리비전) 기록 및 해당 리비전에 대한 작업 내역 재생 가능



   구름 IDE 는 국내에서 개발된 웹 IDE입니다. 해외의 주요 웹 IDE에 비해서는 아직 안정성이나 기능적인 면에서 조금 부족한 모습이 보입니다. Git이나 Subversion과 같은 버전 관리 시스템과의 연동은 불안정한 부분이 있구요.(연결이 되기도 했다가 안되었다가..)

   전반적인 기능으로 봤을 때는 실제 개발을 위한 IDE라기 보다는 교육용에 가까운 모습입니다. 프로젝트 생성 시 자바의 문법이나 특정 API와 관련된 샘플 템플릿을 제공한다거나, IDE 편집 화면 내에서 Slideshare.net의 링크나 컨테이너에 업로드한 PDF 파일을 볼 수 있는 뷰어를 제공합니다.

   구름 IDE 역시 Cloud 9과 비슷하게 로컬 IDE와 유사한 수준의 디버깅 기능을 제공합니다.



구름 IDE 아키텍처



주요 화면 캡처



로그인 후에 가상 머신(컨테이너)의 상태를 확인할 수 있습니다.

- 컨테이너를 실행 시켜야 편집 모드를 사용할 수 있습니다.





새 프로젝트 생성 - Java 프로젝트, 예제 프로젝트의 모습이 보입니다.





편집 화면 구성 - 브라우징, 편집기, 채팅, 쉘 등의 기능을 제공





문서 뷰어 - Slideshare.net의 링크를 입력하거나, PDF 파일을 열어서 내용을 볼 수 있음





문서 뷰어 - PDF 파일을 연 모습







II. 오픈소스 웹 IDE


  주요 오픈소스 웹 IDE


- Eclipse Che

- Eclipse Orion

- Eclipse Dirigible

- Eclipse Flux

- Codebox

- Codiad


위 목록에서 보다시피 Eclipse가 붙은 프로젝트가 네 가지가 있습니다. 이름대로 이클립스 재단에서 클라우드 환경에 맞는 IDE 개발을 위해 진행중인 ECD(Eclipse Cloud Development)에  포함된 네 개의 프로젝트입니다. 


http://www.eclipse.org/ecd 에 접속해 보시면 위 네 개의 프로젝트에 대한 소개와 링크를 제공합니다.






  Eclipse Che - http://www.eclipse/che


Eclipse Che 프로젝트는 상용 서비스인 Codenvy에서 이클립스 재단에 기여한 프로젝트입니다.

Codenvy의 UI를 포함하여 대부분의 기능을 제공하고 있습니다.


그러므로 Eclipse Che에 대한 설명은 생략하겠습니다. (...?)





  Eclipse Orion - https://orionhub.org/


오리온의 주요 특징은 아래와 같습니다. 

   - CloudFoundry와 연동 제공

   - 기본적인 수준의 편집 기능 제공

   - 외부 Git 서비스 연동

   - Shell 제공 (지정된 명령어만 사용 가능, 일반적인 터미널과 다름)

   - Public URL 제공

   - manifest.yml 편집 및 유효성 검사 기능 제공


manifest.yml 파일은 CloudFoundry에서 사용하는 애플리케이션의 설정 파일이며, 애플리케이션의 자원(메모리, 디스크), 서비스, 애플리케이션 속성 등을 관리하는 파일입니다.

Eclipse Orion 에서는 manifest.yml 파일을 직접 이용하여 애플리케이션에 대한 설정을 할 수 있도록 되어있으며, 편집기 자체에서 매니페스트 파일의 유효성을 검사합니다. 즉 CloudFoundry에 최적화된 모습을 보입니다. 컬러 테마 역시 CF와 같아 보이는군요. Pivotal 쪽과 관계가 있지 않을까 추측해 봅니다.




주요 화면 캡처



(작아서 잘 안보이지만..) manifest.yml 파일 편집





Git Repository 연동





Git Repository 연동 후 - 커밋 내역이나 파일을 탐색할 수 있습니다.





쉘 - help 명령어를 쳐서 나오는 명령어만 사용가능합니다.





라우팅 설정





라우팅 목록





Cloud Foundry 연동 - API/Manage URL을 입력하여 연동할 수 있습니다.





플러그인 목록 - CF이 외에도 이미지 뷰어 자바스크립트 툴 등의 다양한 플러그인을 제공합니다.






  Eclipse Dirigible - http://www.dirigible.io


Eclipse Dirigible의 주요 특징은 아래와 같습니다.

   - SAP에서 이클립스 재단에 기여

   - Perspective를 통한 화면 구성

      - Database, Debug, Generic, Registry, Repository, Workspace, Help

   - Sandbox를 통한 호스팅 제공

   - 데이터베이스 스키마 편집 모드(Perspective) 제공

   - Plugin Lazy Load

   - UI 구성이 일반적인 IDE와 차이가 있어서 러닝 커브가 있음


IDE로써의 다양한 기능을 제공합니다. 특히 데이터베이스 스키마 편집을 위한 Database 퍼스펙티브와 사용자들이 개발한 프로젝트를 등록하여 공유할 수 있는 Registry를 제공합니다. 이 두 가지 관점에서는 다른 IDE보다 우수하네요.

다만 Plugin Lazy Load 때문에 체감 속도는 더 느리게 느껴집니다. (매번 로딩...) 또한 전반적으로 UI 반응성이 떨어져서 사용성이 떨어지는 느낌입니다. 


주요 화면 캡처


메인 화면 - 기능별로 구분되어 있습니다만 써보기 전엔 이게 뭔지 잘 모를..



IDE 퍼스펙티브 - 로컬 IDE와 유사하게 잘 구성되어 있습니다. 구문 강조 등의 기본적인 편집기 기능을 제공합니다.





데이터베이스 퍼스펙티브 - MySQL 워크벤치처럼 스키마/테이블을 편집할 수 있습니다.





Registry - 사용자들이 등록한 프로젝트를 볼 수 있습니다. 공유된 걸 가져와서 테스트 해보려고 했으나 런타임 익셉션이..





List와 Generic - 정확하게 어떤 기능인지는 모르겠지만 프로퍼티 관리 기능으로 보입니다.





도움말






  Codebox - http://www.codebox.io


Codebox는 분석 전에 스크린샷이나 소개 글로 봤을 땐 개인적으로 괜찮아 보였는데... 테스트 서버에 회원가입 제한이 되어있더군요. (분석할 때는 작년 말이었는데, 지금도 보니 회원가입은 비활성화 되어있네요)


간략하게 특징을 살펴보면

   - Node.js 기반의 Apache 라이선스로 개발

   - Java, Python, Ruby, Go 등의 언어 지원


마지막 커밋이 글을 쓰는 현재(`16. 3. 31) 11개월 전인 것으로 보아 개발이 중지된 것으로 보입니다. (왠지 슬픔 ㅠㅠ)



편집 화면 캡처

사실 이 캡처의 모습이 서브라임과 유사해서 좋았었는데, 조금 아쉽네요.








  결론


   웹 IDE를 통해서 개발 시 소스코드나 리소스 등의 파일은 서버에 위치해 있고, 필요 시 로컬로부터 업로드하여 사용합니다. 물론 컴파일(빌드), 테스트, 배포 등의 작업 역시 서버 내에서 이루어집니다.


   화면으로 제공되는 것 이외에는 모든 작업이 서버에서 이루어지지만, 서버나 VM/Container에서 사용할 수 있는 기능이 모두 UI로 제공되지는 않습니다. 그걸 다 제공한다면 UI는 너무 복잡해 지겠지요.

이런 제한점을 해소할 수 있는 것은 웹 기반 SSH 터미널을 통해 필요한 것을 설치하거나 사용할 수 있습니다. 물론 SSH를 제공하지 않으면 UI로 제공하는 기능이 기능이자 제약이 되기도 합니다.


   속도 면에서는 서비스가 지원하는 성능 또는 과금 모델에 따라 속도 차이가 날 수 있으며, 인터페이스 및 편집기 기능을 제공하는 웹 브라우저에서는 일반적인 로컬 IDE보다는  속도가 느립니다.

네이티브 앱과 하이브리드 앱(또는 웹 앱)에서의 속도차이와 같은 개념으로 볼 수 있겠네요. 현재는 웹 IDE를 기반으로 실제 운영을 위한 서비스를 개발하라고 하면 당연히 무리가 있겠지만 속도, 기술, 기능 등의 면은 시간이 흐를 수록 발전되고 더 나은 사용자 경험을 제공할 수 있다고 봅니다.


   또한 웹 IDE는 클라우드 개발환경이 IaaS에서 PaaS로 점차 넘어가는 환경에서 이에 걸맞은 개발환경이 될 수 있다고 생각하기 때문에 PaaS와 웹 IDE는 유사한 수준으로 함께 발전할 것이라고 생각됩니다.

   개발 환경을 완전히 웹 IDE를 통해 구축할 수 있다면, 그야말로 언제든, 어디서든 개발할 수 있는 환경이 마련되겠네요 :)






※ http://www.slideshare.net/ssusercef361/ide-60399120 업로드 자료



QueryDSL 설정 시 Maven APT Plugin을 이클립스에서 사용 시 이슈가 있어서 아래와 같은 오류 메시지를 확인할 수 있다.


You need to run build with JDK or have tools.jar on the classpath.If this occures during eclipse build make sure you run eclipse under JDK as well


첫 번째 해결 방법으로는 커맨드 라인에서 직접 메이븐 빌드를 수행하는 것

mvn generate-sources


두 번째 해결 방법으로는 이클립스 설정 파일에 vm을 직접 설정해주는 것

이클립스가 위치한 폴더의 eclipse.ini (Spring Tool Suite의 경우 sts.ini)에서 아래와 같이 -vmargs 옵션의 위 쪽에 -vm 옵션을 추가해준다.

-vm

C:\Program Files\Java\jdk1.8.0_45\bin\javaw.exe

...

-vmargs


※ 참고 : http://stackoverflow.com/questions/24482259/eclipse-issue-with-maven-build-and-jdk-when-generating-qclasses-in-querydsl

Jenkins에서 SSH 서버 추가 시 다음과 같은 오류가 발생하면서 [Test configuration]을 수행할 수 없을 때


Connected, but failed to setup SFTP - check the SSH server. Exec commands should work, but transferring files will fail

jenkins.plugins.publish_over_ssh.BapSshSftpSetupException: Failed to connect SFTP channel. Message [4: Received message is too long: 1027423515]


위 문제는 SSH를 non-interactive로 세션을 접속했을 때 쉘 초기화 파일에서 표준 출력이 있는 경우에 발생한다.

접속하는 계정의 ~/.profile, ~/.bashrc, ~/.bash_profile과 같은 쉘 세션 초기화 시에 실행되는 스크립트에서 echo 명령어나 다른 명령어로 인해 출력이 있을 때 발생할 수 있다. 이 출력 값으로 인해 SSH 클라이언트나 scp, sftp에서 정상적으로 출력되지 않는 경우가 있으므로, 위 초기화 파일로 인해 출력되는 내용이 없도록 수정하면 된다.


확인 방법은 대상 SSH 서버를 원격 호스트로 하여 아래와 같은 명령을 수행해서 아무런 출력이 없음을 확인하면 된다. (/usr/bin/true 파일의 존재는 상관없다, 파일이 없을 때는 bash: /usr/bin/true: No such file or directory라고만 출력된다.)


$ ssh localhost /usr/bin/true

bash: /usr/bin/true: No such file or directory


내 경우는 쉘 접속 시 초기화 스크립트의 수행 순서를 확인하려고 .bashrc 파일에 echo "=== START ~/.bashrc ==="를 넣어두었는데, 이것 때문에 젠킨스에서 SSH 서버 설정을 테스트할 수 없었다. 위 구문만 지워주니 깔끔하게 success로 확인이 되었다.



참조 : OpenSSH FAQ - 2.9 sftp/scp is fails at connection, but ssh is OK

+ Recent posts