@Scheduled에서 Thread관리하기

@Scheduled에서 외부 서비스에 데이터를 처리요청할때 데이터를 중복으로 처리하는 문제가 발생할수 있다.

Scheduled는 매번 실행할때마다 빠른 처리를 위해 fixedDelayString=”1000″ 으로 설정하였고, 한번에 실행되는 thread의 수는 5개로 제한하였다. 또한 Scheduled 한번실행에 처리되지 않은 데이터 1000건을 외부 API를통해 가져와 처리하도록 설계하였다.

이때 발생할수 있는 문제는 1000개의 데이터를 처리(Thread가 종료) 하기전에 Scheduled가 실행되면서 1000개의 데이터를 외부API에 다시 요청하게 되고 1000개의 데이터에는 첫번째 가져온 아직 처리되지 않은 데이터가 포함되어 중복 처리되면서 오류가 발생한다.
한번의 Scheduled의 실행으로 1000개의 데이터를 5개의 Thread가 모두 처리(종료)될때까지 대시 시켜 해당 문제를 해결할수 있는데 방법은 아래와 같다.

// Thread로 실행할 Class
public class Job implements Callable<Void> {
  
  @Override
  public Void call() throws Exception {
    // 외부 API 호출
    // 외부 API를 사용한 데이터 처리요청
    return null;
  }
}

// Scheduled Class
public class MyScheduler {
  
  @Scheduled(fixedDelay = 1000)
  public void executeJob() {
  

    // Thread 개수
    int threadSize = 5;
  
    // 외부에서 처리 데이터 가져오기
    int totalTasks = 1000;
  
    ExecutorService executor = Executors.newFixedThreadPool(threadSize);
    List<Callable<Void>> tasks = new ArrayList<>();
    
    // 모든 작업이 완료될때까지 대기
    for (int i=0; i < totalTasks; i++) {
      Callable<Void> task = new Job();
      tasks.add(task);
    }
    
    try {
      List<Future<Void>> results = executor.invokeAll(tasks);
      
      for (Future<Void> result : results) {
        // 작업 결과 처리
        result.get();
      }
    } catch (InterruptedException | ExcutionException e) {
      // 예외처리
    } finally {
      // ExecutorService 종료
      executor.shutdown();
    }
  }
}

위 코드처럼 ExecutorService를 사용하여 Callable에 작업요청하고 결과를 기다린다.
invokeAll을 사용하여 작업이 완료될때까지 대기하고, 작업결과를 얻어 처리할수 있다. 이렇게 하면 모든 스레드가 종료될때까지 대기한 다음 스케줄링을 실행할수 있다.

invokeAll(Collection> tasks)
여러개의 Callable 타입 작업을 동시에 스레드풀에서 실행하고, 모든 작업이 완료될때까지 기다린다.

invokeAny(Collection> tasks)
여러개의 Callable 타입 작업중 하나라도 완료되면 결과를 반환하고, 다른작업들은 취소한다.

shutdown()
스레드 풀을 종료하고 스레드 풀에 할당된 리소스를 해제한다. 작업이 완료되지 않은 경우에도 스레드 풀을 종료한다.

shutdownNow()
스레드 풀을 즉시 종료하고 아직 시작되지 않은 작업을 취소한다. 작업이 이미 실행중인경우에는 종료되지 않을수 있다.

Command failed with error 13 (Unauthorized)

com.mongodb.MongoCommandException: Command failed with error 13 (Unauthorized): ‘not authorized on hie to execute command

MongoDB에서 사용자 정의 function을 사용할때 발생하는 문제로 테스트시 사용된 MongoDB의 버전은 4.0.14이다.

function에서 처리하는 일은 A Collection의 특정 document를 B Collection으로 복사하는 일을 한다.

// function 생성
function copyDocument(documentId) {
  var doc = db['a_collection_name'].findOne(_id:documentId);
  if (doc) {
    db['b_collection_name'].insertOne(doc);
  }
}

db.system.js.save({
  _id: "copyDocument",
  value: copyDocument
});

// function 실행
// 방법1
db.runCommand({ eval: "copyDocument('documentId')" });
// 방법2
db.eval("copyDocument(ObjectId('documentId'))");

실행결과는 아래와 같다.

{
    "ok" : 0.0,
    "errmsg" : "not authorized on hie to execute command ...",
    "code" : 13,
    "codeName" : "Unauthorized"
}

function호출에 대한 사용자 권한이 없어 발생한 문제이다.

사용자에게 추가 권한 부여

현재 사용자에게는 readWrite권한이 부여되어 있는 상태로 모든 query를 실행하는데 문제가 없는 상태이지만 Admin권한이 누락되어 발생하는 오류일수 있으므로 dbAdmin, dbUser 권한을 추가로 부여한다.

db.grantRolesToUser('username', [{ role: "dbAdmin", db: "database" }]);
db.grantRolesToUser('username', [{ role: "dbUser", db: "database" }]);

사용자에게 추가 권한을 부여한 후에도 동일한 오류가 발생!

Mongod.conf 설정 변경

사용자에게 권한을 추가로 부여한 후에도 동일한 문제가 발생하고 있어 찾아본 결과 mongod.conf 설정파일의 설정을 변경해줘야 한다고 한다.

# 보안 설정 (mongod.conf)
security:
  authorization: enabled

하지만 설정파일을 열어보니 설정하는 방식이 좀 달랐고, 이미 설정되어 있는것으로 보인다.

# Turn on/off security.  Off is currently the default
#noauth=true
auth=true
keyFile=/home/ec2-user/mongodb-keyfile

mongDB에서 function사용에 대한 권한문제를 계속 찾아보던중 eval 사용에 대한 보안 이슈가 있으므로 주의를 요한다는 문구가 많이 보인다.

방향을 틀어 그럼 function을 사용하지 않고 function의 역할을 할수 있는 방법을 찾아보니 aggregate를 사용한 방법을 찾을수 있었다.

Aggregate를 사용하여 function 대체하기

// a_collection에서 documentId를 찾아 b_collection으로 복사
db.a_collection.aggregate([
  {
    $match: {
      _id: ObjectId(documentId)
    }
  },
  {
    $out: "b_collection"
  }
]);

위 방법을 통해 function을 사용하지 않고 원하는 처리를 진행할수 있었다.

하지만 function사용시 발생하는 권한문제는 아직 해결되지 않았으며 방법을 찾게 되면 commant예정이다.

MAC에서 PPK파일을 PEM파일로 변환하는 방법

.ppk 키파일과 .pem 키파일을 둘다 SSH 개인키를 저장하는데 사용되는 파일 형식이다.

만약 키파일이 .ppk형식이라면 MAC사용자의 경우 .pem형식으로 변경하여 사용해야 하는데 방법은 아래와 같다.

PUTTY 설치

> brew install putty

키파일 변환

> puttygen private.ppk -O private-openssh -o private.pem

puttygen : PuTTY Key Generator도구를 실행하는 명령어

private.ppk : 변환할 .ppk 개인 키 파일

-O private-openssh : 변환된 개인 키 파일의 형식을 지정하는 옵션으로 OpenSSH형식으로 변환

-o private.pem : 변환된 개인 키를 저장할 파일의 이름과 경로 지정

Spring boot에서 Scheduler 사용하기

Scheduler는 어떤 조건을 갖고 일정 간격 또는 특정 시간에 주기적으로 실행됩니다.

Spring boot에서는 @Scheduled 어노테이션을 사용하여 위와 같은 작업을 진행할수 있습니다.

아래 설정은 스케줄러를 사용하기 위한 최소 설정 내용입니다.

먼저 @Scheduled 어노테이션을 사용하기 위해 스케줄러 활성화가 필요합니다.

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;

@SpringBootApplication
@EnableScheduling
public class Application {
    public static void main(String []  args) {
        SpringApplication.run(Application.class, args);
    }
}

@EnableScheduling 어노테이션을 추가함으로서 스케줄러를 사용할 준비가 되었습니다.

다음은 스케줄러 클래스를 간단하게 작성하도록 합니다.

아래와 같이 코드를 작성하면 5초에 한번씩 console에 Scheduled executed. 문장을 출력할것입니다.

import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

@Component
public class TimeScheduler {

    @Scheduled(initialDelay = 500, fixedDelay = 5000)
    public void execute() {
        System.out.println("Scheduled executed.");
    }
}

@Scheduled 어노테이션에서 사용할수 있는 옵션들을 아래 정리합니다.

자세한 내용은 다음을 차고하세요.

https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/scheduling/annotation/Scheduled.html

// 5초마다 실행
@Schedulred(fixedRate = 5000)

// 이전 실행 완료 후 5초 대기 후 실행
@Schedulred(fixedDelay = 5000)

// 첫 실행시 5초 대기후 실행, 그 이후 5초마다 실행
@Schedulred(initialDelay = 5000, fixedRate = 5000)

// 매일 낮12시에 실행
@Schedulred(cron = “0 0 12 * * ?”

로그인 없이 github 사용하기

원격 저장소에 pull 또는 push를 하기 위해 매번 로그인하는 절차를 거치지 않고 공개키를 사용하여 로그인 없이 원격 저장소를 사용하는 방법입니다.

순서는 아래와 같습니다.

  1. 개인키와 공개키를 생성한다.
  2. github에 공개키를 등록한다.
  3. git clone한다.

먼저 ssh-keygen으로 개인키와 공개키를 생성해야합니다.

> ssh-keygen -t rsa -C "your_email@example.com"

개인키는 외부로 노출하지 않으며 공개키만 사용하면 됩니다.

cat ~/.ssh/id_rsa.pub

위 명령어로 공개키를 확인할수 있습니다.

그다음 공개키를 github에 등록합니다.

로그인후 오른쪽 상단 프로필 사진을 클릭하여 Settings 메뉴를 선택합니다.

왼쪽 메뉴에 SSH and GPG keys를 선택합니다.

New SSH key 버튼을 클락하여 공개키를 등록합니다.

title에는 공개키를 식별할수 있는 제목을 입력해주시고, key필드에 위에서 확인한 공개키를 입력후 Add SSH key 버튼을 눌러 등록해줍니다.

다음으로 git clone을 진행하면 됩니다.

공개키를 사용하여 로그인없이 원격저장소를 사용하기 위해서는 SSH 주소를 사용해야합니다.

Flutter에서 RichText를 사용해 다양한 텍스트 스타일링 적용하기

Flutter는 텍스트 스타일링을 지원하는 RichText 위젯을 제공합니다. RichText를 사용하면 텍스트의 서로 다른 부분에 서로 다른 스타일을 적용할 수 있습니다. 예를 들어, 서로 다른 색상, 글꼴 크기 및 글꼴 스타일을 사용할 수 있습니다. 이를 통해 더욱 다양하고 풍부한 텍스트 효과를 적용할 수 있습니다.

다음은 Flutter에서 RichText를 사용하는 예시 코드입니다. 이 예시 코드에서는 두 개의 TextSpan을 RichText 위젯에 추가하고 각각 서로 다른 스타일을 적용합니다.

RichText(
  text: TextSpan(
    text: 'Flutter is ',
    style: TextStyle(
      color: Colors.black,
      fontSize: 20.0,
      fontWeight: FontWeight.bold,
    ),
    children: <TextSpan>[
      TextSpan(
        text: 'awesome',
        style: TextStyle(
          color: Colors.blue,
          decoration: TextDecoration.underline,
        ),
      ),
      TextSpan(
        text: '!',
        style: TextStyle(
          color: Colors.black,
          fontSize: 20.0,
          fontWeight: FontWeight.bold,
        ),
      ),
    ],
  ),
);

이 예시 코드에서는 RichText 위젯을 생성하고, text 속성을 설정하여 TextSpan 객체를 전달합니다. TextSpan 객체는 첫 번째 TextSpan에는 “Flutter is”라는 텍스트를 나타내는 TextStyle 객체를 포함하고, 두 번째 TextSpan에는 “awesome”라는 텍스트를 나타내는 TextStyle 객체를 포함합니다.

첫 번째 TextSpan의 TextStyle 객체는 글꼴 크기가 20, 굵은 글꼴, 검은색 텍스트로 설정됩니다. 두 번째 TextSpan의 TextStyle 객체는 파란색으로 텍스트를 강조하고 밑줄을 추가합니다.

RichText 위젯은 이 두 개의 TextSpan을 결합하여 “Flutter is awesome!”라는 텍스트를 나타냅니다. 결과적으로, “Flutter is”는 검은색 굵은 글꼴로 표시되고, “awesome”는 파란색 밑줄로 강조된 텍스트로 표시됩니다. 마지막으로, 끝에는 다시 검은색 굵은 글꼴로 된 느낌표가 있습니다.

Flutter의 ElevatedButton 사용법과 특징 설명

Flutter의 ElevatedButton은 일반적인 버튼과는 다르게 3D 효과를 적용하여 눌린 것처럼 보이는 버튼을 만들어줍니다. 이 버튼은 Material Design 스타일의 디자인을 적용할 수 있으며, 버튼의 색상, 텍스트, 그림자 등을 쉽게 커스터마이즈할 수 있습니다.

ElevatedButton은 Material Design 스타일의 디자인 가이드에 따라 생성되며, 버튼이 눌렸을 때 3D 효과를 제공합니다. ElevatedButton은 onPressed 콜백을 제공하여 버튼이 클릭되었을 때 수행되는 작업을 지정할 수 있습니다.

다음은 ElevatedButton을 생성하는 예시입니다.

ElevatedButton(
  onPressed: () {
    // 버튼이 클릭되었을 때 수행될 작업
  },
  child: Text('버튼'),
)

ElevatedButton에는 많은 속성이 있으며, 버튼의 색상, 모양, 그림자 등을 쉽게 변경할 수 있습니다. 예를 들어, ElevatedButton의 색상을 변경하려면 다음과 같이 코드를 작성할 수 있습니다.

ElevatedButton(
  onPressed: () {
    // 버튼이 클릭되었을 때 수행될 작업
  },
  style: ElevatedButton.styleFrom(
    primary: Colors.blue, // 버튼의 배경 색상
  ),
  child: Text('버튼'),
)

이처럼 ElevatedButton은 쉽게 사용할 수 있으며, Material Design 스타일의 3D 버튼을 쉽게 만들어줍니다.

CloudFlare 적용시 /wp-admin 접근 문제

무료 SSL을 사용하고자 찾아보던중 CloudFlare를 알게 되었고, 별 문제 없이 설정을 완료할수 있었다.

cafe24.com의 호스팅을 사용하고 있으며 현재 이 도메인에 wordpress를 설치하여 사용하고 있다.

  • 회원 가입후 서비스 site의 도메인을 등록한다.
  • 등록후 DNS 에서 네임서버를 확인할수 있는데 도메인의 네임서버를 CloudFlare로 변경한다.
  • SSL/TLS 의 설정을 Flexible 또는 Full로 변경한다. 나의 경우 Full로 변경했음.
  • 위 설정후 도메인에 접속해보면 SSL이 적용된것을 확인할수 있다.

일반 페이지는 정상적으로 작동하였지만 관리자 화면으로 들어가기 위해 로그인부터 style이 깨지고 로그인 시도시 forbidden 오류가 발생하였다.

CloudFlare의 설정을 이것저것 찾아보고 변경해봤지만 해결되지 않았고, 해답은 cafe24 설정에서 찾을수 있었다.

cafe24 > 호스팅관리 > 보안관리 > 디렉토리 접속설정 페이지로 들어가 보면 디렉토리 설정 현황 목록에 /wp-admin 폴더가 있었고 설정된 폴더를 제거후 관리자 페이지가 접근할수 있었다.

본인이 설정한 부분은 아니고 cafe24를 통해 wordpress를 설치했고, 어떤 보안상의 이유로 해당 폴더가 설정되어 있던게 아닌가 하는 생각을 한다.

Flutter – scaffold에 전역 스타일 적용

scaffold widget의 appBar에 대해서 전역 스타일일 적용하는 방법

개발하는 앱에서 AppBar를 사용하고 있다면 일괄적으로 style을 적용해야할것이다.

// main.dart

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        scaffoldBackgroundColor: Colors.white,
        appBarTheme: const AppBarTheme(
            backgroundColor: Colors.white,
            titleTextStyle: TextStyle(
              color: Colors.black,
              fontSize: 18,
              fontWeight: FontWeight.w100,
            )),
      ),
      home: const SignUpScreen(),
    );
  }
}

main.dart의 theme: ThemeData를 통해 scaffold 전역 스타일을 지정할수 있다.

예를 들어 appBarTheme: AppBarTheme는 scaffold의 AppBar에 적용되는 스타일을 정의하는데 내용은 아래와 같다.

// appbar의 background color
backgroundColor: Colors.white

// title 속성의 Text 스타일
ttitleTextStyle: TextStyle(
color: Colors.black,
fontSize: 18,
fontWeight: FontWeight.w100,
)

Flutter – svg 파일 사용하기

https://pub.dev/packages/flutter_svg

일반 이미지처럼 사용하면 될줄 알고 사용하니 계속 오류를 토해내고 있어 검색을 해보니 svg파일을 사용하기위해서 별도의 package를 사용해야했다.

svg 파일 사용하기 위해 dependencie를 추가합니다.

# pubspec.yaml 파일에 flutter_svg dependencie를 추가.
dependencies:
  flutter_svg: ^1.1.6

// example
child: Container(
  child: SvgPicture.asset(
    'assets/img.svg',
    width: 100,
    height: 100,
  ),
),