KINTOテクノロジーズのブログ - TECH PLAY

TECH PLAY

KINTOテクノロジーズ

KINTOテクノロジーズ の技術ブログ

1113

An Issue We Encountered During Testing With Spring Batch using DBUnit Introduction Hello. I am Takehana from the Payment Platform Team, Common Service Development Group[^1][^2][^3][^4][^5][^6] at the Platform Development Division. This time, I would like to write about an issue that we encountered while testing with Spring Batch + DBUnit. Environment Libraries, etc. Version Java 17 MySQL 8.0.23 Spring Boot 3.1.5 Spring Boot Batch 3.1.5 JUnit 5.10.0 Spring Test DBUnit 1.3.0 Encountered Issues We are using DB unit for testing Spring Boot 3 with Spring Batch. The Batch process follows the Chunk model, where ItemReader performs DB searches, and ItemWriter updates the DB. Given this setup, when running tests with data volumes exceeding the Chunk size, the tests did not complete... Investigations and Attempts Observations Code new StepBuilder("step", jobRepository) .<InputDto, OutputDto>chunk( CHUNK_SIZE, transactionManager) .reader(reader) .processor(processor) .writer(writer) .build(); I was testing a batch with the steps mentioned above as follows. @SpringBatchTest @SpringBootTest @TestPropertySource( properties = { "spring.batch.job.names: Foobar-batch", "targetDate: 2023-01-01", }) @Transactional(isolation = Isolation.SERIALIZABLE) @TestExecutionListeners({ DependencyInjectionTestExecutionListener.class, DirtiesContextTestExecutionListener.class, TransactionDbUnitTestExecutionListener.class }) @DbUnitConfiguration(dataSetLoader = XlsDataSetLoader.class) class FoobarBatchJobTest { @Autowired private JobLauncherTestUtils jobLauncherTestUtils; @BeforeEach void setUp() { } @Test @DatabaseSetup("classpath:dbunit/test_data_import.xlsx") @ExpectedDatabase( value = "classpath:dbunit/data_expected.xlsx", assertionMode = DatabaseAssertionMode.NON_STRICT_UNORDERED) void launchJob() throws Exception { val jobExecution = jobLauncherTestUtils.launchJob(); assertEquals(ExitStatus.COMPLETED, jobExecution.getExitStatus()); } } When I set the test data to be less than the chunk size, the test passed without any issues. However, when the test data exceeded the chunk size, the test froze and never completed. (This occurred even with a chunk size of 1 and a data count of 1) Suspecting the issue might be on DB connections, I noted that Spring Batch treats each chunk as a single transaction. If processing in parallel, it would require more DB connections than the number of concurrent executions. So I adjusted the pool size to test this hypothesis. spring: datasource: hikari: maximum-pool-size: I changed 10 to 100 among other adjustments, but the issue was still not resolved… Start debugging I set up debug logs and ran the application to observe the behavior. The execution seemed to stop at the log output on line 88 of org.springframework.batch.core.step.item.ChunkOrientedTasklet . So, I set a breakpoint to verify. I then reached line 408 of org.springframework.batch.core.step.tasklet.TaskletStep . It appeared that the semaphore couldn’t acquire a lock (= waiting for the lock to be released), causing the execution to halt there. Delving deeper into Spring Batch Continuing my investigation, I traced the flow of execution in the step processing. The rough outline of the relevant parts is as follows. Execute doExecute of TaskletStep Create a semaphore Pass the semaphore to ChunkTransactionCallback , which is an implementation of TransactionSynchronization , link it with the transaction execution, and configure it in RepeatTemplate Step processing begins for the chunk The semaphore is locked in doInTransaction of TaskletStep Execute the main step processing The commit is executed by TransactionSynchronizationUtils` The AbstractPlatformTransactionManager ’s triggerAfterCompletion method is called, and the in-process invokeAfterCompletion` is executed. The semaphore is released in the afterCompletion method of the ChunkTransctionCallback by the invokeAfterCompletion. If data remains, return to 4 During this test run, the semaphore of 9 was not released, and it passed through 4 again and ended up freezing at 5 . Why was the semaphore not released...? During the review mentioned above, at Step semaphore release , I found the following condition in the relevant code. status.isNewSynchronization() did not become true , so invokeAfterCompletion was not executed. org.springframework.transaction.support.DefaultTransactionStatus#isNewSynchronization is as follows: /** * Return if a new transaction synchronization has been opened * for this transaction. */ public boolean isNewSynchronization() { return this.newSynchronization; } It returns whether a new transaction synchronization has been opened for this transaction. Considerations The current situation is that we haven’t fully traced yet why isNewSynchronization doesn’t become true . However, I thought I might be able to find some clues in the logs from our various trial and error attempts. If @Transactional is not applied to the test class 2024-03-27T08:57:14.527+0000 [Test worker] TRACE o.s.t.i.TransactionInterceptor - Completing transaction for [org.springframework.batch.core.repository.support.SimpleJobRepository.update] Foobar-batch 19 2024-03-27T08:57:14.527+0000 [Test worker] DEBUG o.s.orm.jpa.JpaTransactionManager - Initiating transaction commit Foobar-batch 19 2024-03-27T08:57:14.527+0000 [Test worker] DEBUG o.s.orm.jpa.JpaTransactionManager - Committing JPA transaction on EntityManager [SessionImpl(1075727694<open>)] Foobar-batch 19 2024-03-27T08:57:14.534+0000 [Test worker] DEBUG o.s.orm.jpa.JpaTransactionManager - Closing JPA EntityManager [SessionImpl(1075727694<open>)] after transaction Foobar-batch 19 2024-03-27T08:57:14.536+0000 [Test worker] DEBUG o.s.b.repeat.support.RepeatTemplate - Repeat operation about to start at count=2 Foobar-batch 19 If @Transactional is applied to the test class 2024-03-27T09:04:04.600+0000 [Test worker] TRACE o.s.t.i.TransactionInterceptor - Completing transaction for [org.springframework.batch.core.repository.support.SimpleJobRepository.update] Foobar-batch 20 2024-03-27T09:04:04.601+0000 [Test worker] DEBUG o.s.b.repeat.support.RepeatTemplate - Repeat operation about to start at count=2 Foobar-batch 20 When @Transactional is applied, "Initiating transaction commit..." from JpaTransactionManager with @Transactionalis not being logged. The test class uses TransactionalTestExecutionListener and executes within the same transaction using @Transactional . This ensures that the test data registered with DBUnit is accessible to code under test and is rolled back after the test is completed. However, I concluded that isNewSynchronization does not become true because existing transactions are being reused (a new transaction is not started) when the same step is executed. Workaround As a brute-force workaround to avoid using TransactionalTestExecutionListener , I performed the cleanup manually after each test, which successfully prevented the freeze. class FoobarTestExecutionListenerChain extends TestExecutionListenerChain { private static final Class<?>[] CHAIN = { FoobarTransactionalTestExecutionListener.class, DbUnitTestExecutionListener.class }; @Override protected Class<?>[] getChain() { return CHAIN; } } class HogeTransactionalTestExecutionListener implements TestExecutionListener { private static final String CREATE_BACKUP_TABLE_SQL = "CREATE TEMPORARY TABLE backup_%s AS SELECT * FROM %s"; private static final String TRUNCATE_TABLE_SQL = "TRUNCATE TABLE %s"; private static final String BACKUP_INSERT_SQL = "INSERT INTO %s SELECT * FROM backup_%s"; private static final List<String> TARGET_TABLE_NAMES = List.of( "Foobar", "fuga", "dadada"); /** * Create a test working table * * @param testContext * @throws Exception */ @Override public void beforeTestMethod(TestContext testContext) throws Exception { val dataSource = (DataSource) testContext.getApplicationContext().getBean("dataSource"); val jdbcTemp = new JdbcTemplate(dataSource); // Backup existing data to a temporary table before testing TARGET_TABLE_NAMES.forEach( tableName -> jdbcTemp.execute(String.format(CREATE_BACKUP_TABLE_SQL, tableName, tableName))); // Table initialization TARGET_TABLE_NAMES.forEach( tableName -> jdbcTemp.execute(String.format(TRUNCATE_TABLE_SQL, tableName))); } /** * Drop the test working table * * @param testContext * @throws Exception */ @Override public void afterTestMethod(TestContext testContext) throws Exception { val dataSource = (DataSource) testContext.getApplicationContext().getBean("dataSource"); val jdbcTemp = new JdbcTemplate(dataSource); // Restore the table TARGET_TABLE_NAMES.forEach( tableName -> jdbcTemp.execute(String.format(TRUNCATE_TABLE_SQL, tableName, tableName))); TARGET_TABLE_NAMES.forEach( tableName -> jdbcTemp.execute(String.format(BACKUP_INSERT_SQL, tableName, tableName))); } } Remove TransactionDbUnitTestExecutionListener and avoid using TransactionalTestExecutionListener. (Use DbUnitTestExecutionListener to lode the test data from Excel) Create a custom TestExecutionListener and move data from the target table to a temporary table during pre-processing, then restore it after the test. beforeTestMethod is executed before the test method, and afterTestMethod is executed after the test method. This approach made it possible to run tests while preserving Spring’s transaction management. Impressions Despite extensive searches, I couldn’t find satisfactory information, leaving the issue in a state of uncertainty. However, by looking further into the Spring Boot source code, I made various discoveries and it turned out to be a valuable learning experience through code reading. (Although I haven’t fully grasped everything yet…) I was wondering if I was fundamentally misunderstanding how to use Spring and the test libraries, questioning whether I was implementing them correctly based on the library creators’ assumptions and if there were more suitable classes available. This has highlighted that I still have much to learn. I would like to continue to approach exploration and improvement with the same curiosity, asking, “How does this work?” Thank you for reading this article. I hope this will be helpful to others facing similar issues. [^1]: Post 1 by a member of the Common Service Development Group [ Domain-Driven Design (DDD) incorporated in a payment platform intended to allow global expansion ] [^2]: Post 2 by a member of the Common Service Development Group [ Remote Mob Programming: How a Team of New Hires Achieved Success Developing a New System Within a Year ] [^3]: Post 3 by a member of the Common Service Development Group [ Efforts to Improve Deploy Traceability to Multiple Environments Utilizing GitHub and JIRA ] [^4]: Post 4 by a member of the Common Service Development Group [ Creating a Development Environment Using VS Code's Dev Container ] [^5]: Post 5 by a member of the Common Service Development Group [ Spring Boot 2 to 3 Upgrade: Procedure, Challenges, and Solutions ] [^6]: Post 6 by a member of the Common Service Development Group [ Guide to Building an S3 Local Development Environment Using MinIO (RELEASE.2023-10) ]
Introduction Hello. I am Nakaguchi from KINTO Technologies' Mobile App Development Group. I lead the iOS team for the KINTO Easy Application app which I will refer to as “the iOS team” in this article for convenience. We hold Retrospectives irregularly, but I find that they can be rather challenging. Am I succeeding in bringing out everyone's true feelings?? What are the team's real challenges?? Is my facilitation effective?? etc. I recently watched a webinar by Classmethod, Inc. and was so impressed by their session on "How to Build a Self-Managed Team" that I decided to apply for another training session they introduced in it about Retrospectives. In this article, I'll share my experience attending that session. Pre-Alignment Session Before the Retrospective, we had a meeting with Mr. Abe and Mr. Takayanagi from Classmethod. In order to hold Retrospectives that were best suited to our team’s situation, we discussed the current status of the iOS team with them for nearly an hour. Overview of the Retrospective On the day of the Retrospective, Mr. Takayanagi and Mr. Ito came to the company to facilitate the meeting. The meeting lasted for about two hours and followed this general flow: Self-introductions Aligning the purpose of our Retrospectives Individual exercise on “How to make the team a little bit better” Same content as above but in pairs Sharing the findings with the whole team Thinking about specific action plans in pairs Sharing the findings with the whole team Closing First Half Out of the almost two-hours meeting, it is worth noting that about half of the time was spent on "1. Self-introductions" and "2. Aligning the purpose of our Retrospectives". During the segment "1. Self-introductions", the facilitators asked us questions such as our names or nicknames, our roles in the team, or the extent of our interactions with other team members. They looked not only at the atmosphere of the team and the personality of each of us, but also at the relationships and compatibility between team members. During "2. Aligning the purpose of our Retrospectives", I got everyone to agree on what can be done to make the current team a little better , which was a topic I had requested. After a major release last September, our team is currently focused on improving features and refactoring, so although we are in a less busy spot, it seems that it is no easy feat to improve teams in our situation to make them a little better . I also explained the purpose, role, and expectations for each participant that I, as the meeting organizer, had in mind when inviting them. I was told that this helps clarify how everyone should participate and makes it easier for them to speak up. I think it was a good opportunity for me to talk about things that I usually don’t have the right timing for or that I can’t speak about directly. By spending this time in the first half of the meeting, we were able to create an atmosphere where it was easy for everyone to speak, and I felt that overall rapport was greatly improved. Facilitation Second Half After thinking about " 3. Making the team a little better" individually, we proceed on with the work. However, we didn’t use any framework related to retrospectives. Instead, we simply wrote down what could make the team a little better on sticky notes. We did individual work and then moved on to pair work. There are situations where pair work is beneficial and others where it is not. In this case, it seemed like the team benefited from it. Also, the combination of people is key, as it is important not to cause psychological strain amongst the participants. Pair Work After that, everyone gave presentations, and there were many opinions that I was not able to draw out in the Retrospectives I have held so far. I felt that I was able to draw them out through the rapport we built and the pair work in the first half. Then, based on the opinions that came up, everyone was asked to think about what specific actions should be taken and 6. Thinking about specific action plans in pairs. Then, each team presented their ideas. Presentation As a result, we decided to implement the following actions: Creating a Slack channel Having a place where everyone can chat freely Setting up a weekly meeting dedicated to chatting We could build more trust by talking more about ourselves, so we decided to create a private channel instead of a public one. Trying to gather together at meeting rooms as much as possible (as many people used to attend online to meetings from their desks even if they were in the office). Setting up guideline consultation meetings regarding assigned tasks Clearly stating the deadline on the task tickets We are addressing these issues as quickly as possible, starting the next day. Closing At the end of the meeting, Mr. Takayanagi talked about the importance of customizing meetings, such as understanding the time allocation of meetings, the characteristics of participants and to draw their opinions. In particular, at this Retrospective, he focused his facilitation on people , using a lot of pair work. Closing Post-Retrospective Survey Results Here are the results of the feedback survey taken after the Retrospective (out of 10 responses). Change in evaluation Before: 6.3 -> After: 9 NPS 80 (What is NPS?) AI summary of "How satisfied did you feel after you participated?" (free text) The survey results showed that participants were happy with the session and the facilitator's explanations. In addition, there were many positive comments about how specific decisions were made that led to the next actions. Furthermore, the opportunity to understand the thoughts of other team members, and the ability to listen to things that are not normally heard, were also highly evaluated. These results suggest that the meeting was meaningful for everyone. ** Just being above 0 was a great, but there was a whopping NPS of 80! ** Final Thoughts Through this Retrospective, I realized that there were many members who felt that there was a lack of communication, and we were able to focus on the next course of action so it was a very fulfilling Retrospective. I was happy to see from the questionnaire results that the participating members were also satisfied. I also realized that the role of the meeting facilitator is very important. This is a very advanced skill that cannot be acquired overnight, and I think that the organization should focus on developing and acquiring such skills. To start with, I would like to study facilitation and become able to conduct better meetings.
Introduction Hello. I am Nakaguchi from KINTO Technologies' Mobile App Development Group. I lead the iOS team for the KINTO Easy Application app which I will refer to as “the iOS team” in this article for convenience. We hold Retrospectives irregularly, but I find that they can be rather challenging. Am I succeeding in bringing out everyone's true feelings?? What are the team's real challenges?? Is my facilitation effective?? etc. I recently watched a webinar by Classmethod, Inc. and was so impressed by their session on "How to Build a Self-Managed Team" that I decided to apply for another training session they introduced in it about Retrospectives. In this article, I'll share my experience attending that session. Pre-Alignment Session Before the Retrospective, we had a meeting with Mr. Abe and Mr. Takayanagi from Classmethod. In order to hold Retrospectives that were best suited to our team’s situation, we discussed the current status of the iOS team with them for nearly an hour. Overview of the Retrospective On the day of the Retrospective, Mr. Takayanagi and Mr. Ito came to the company to facilitate the meeting. The meeting lasted for about two hours and followed this general flow: Self-introductions Aligning the purpose of our Retrospectives Individual exercise on “How to make the team a little bit better” Same content as above but in pairs Sharing the findings with the whole team Thinking about specific action plans in pairs Sharing the findings with the whole team Closing First Half Out of the almost two-hours meeting, it is worth noting that about half of the time was spent on "1. Self-introductions" and "2. Aligning the purpose of our Retrospectives". During the segment "1. Self-introductions", the facilitators asked us questions such as our names or nicknames, our roles in the team, or the extent of our interactions with other team members. They looked not only at the atmosphere of the team and the personality of each of us, but also at the relationships and compatibility between team members. During "2. Aligning the purpose of our Retrospectives", I got everyone to agree on what can be done to make the current team a little better , which was a topic I had requested. After a major release last September, our team is currently focused on improving features and refactoring, so although we are in a less busy spot, it seems that it is no easy feat to improve teams in our situation to make them a little better . I also explained the purpose, role, and expectations for each participant that I, as the meeting organizer, had in mind when inviting them. I was told that this helps clarify how everyone should participate and makes it easier for them to speak up. I think it was a good opportunity for me to talk about things that I usually don’t have the right timing for or that I can’t speak about directly. By spending this time in the first half of the meeting, we were able to create an atmosphere where it was easy for everyone to speak, and I felt that overall rapport was greatly improved. Facilitation Second Half After thinking about " 3. Making the team a little better" individually, we proceed on with the work. However, we didn’t use any framework related to retrospectives. Instead, we simply wrote down what could make the team a little better on sticky notes. We did individual work and then moved on to pair work. There are situations where pair work is beneficial and others where it is not. In this case, it seemed like the team benefited from it. Also, the combination of people is key, as it is important not to cause psychological strain amongst the participants. Pair Work After that, everyone gave presentations, and there were many opinions that I was not able to draw out in the Retrospectives I have held so far. I felt that I was able to draw them out through the rapport we built and the pair work in the first half. Then, based on the opinions that came up, everyone was asked to think about what specific actions should be taken and 6. Thinking about specific action plans in pairs. Then, each team presented their ideas. Presentation As a result, we decided to implement the following actions: Creating a Slack channel Having a place where everyone can chat freely Setting up a weekly meeting dedicated to chatting We could build more trust by talking more about ourselves, so we decided to create a private channel instead of a public one. Trying to gather together at meeting rooms as much as possible (as many people used to attend online to meetings from their desks even if they were in the office). Setting up guideline consultation meetings regarding assigned tasks Clearly stating the deadline on the task tickets We are addressing these issues as quickly as possible, starting the next day. Closing At the end of the meeting, Mr. Takayanagi talked about the importance of customizing meetings, such as understanding the time allocation of meetings, the characteristics of participants and to draw their opinions. In particular, at this Retrospective, he focused his facilitation on people , using a lot of pair work. Closing Post-Retrospective Survey Results Here are the results of the feedback survey taken after the Retrospective (out of 10 responses). Change in evaluation Before: 6.3 -> After: 9 NPS 80 (What is NPS?) AI summary of "How satisfied did you feel after you participated?" (free text) The survey results showed that participants were happy with the session and the facilitator's explanations. In addition, there were many positive comments about how specific decisions were made that led to the next actions. Furthermore, the opportunity to understand the thoughts of other team members, and the ability to listen to things that are not normally heard, were also highly evaluated. These results suggest that the meeting was meaningful for everyone. Just being above 0 was a great, but there was a whopping NPS of 80! Final Thoughts Through this Retrospective, I realized that there were many members who felt that there was a lack of communication, and we were able to focus on the next course of action so it was a very fulfilling Retrospective. I was happy to see from the questionnaire results that the participating members were also satisfied. I also realized that the role of the meeting facilitator is very important. This is a very advanced skill that cannot be acquired overnight, and I think that the organization should focus on developing and acquiring such skills. To start with, I would like to study facilitation and become able to conduct better meetings.
​KINTOサービスの認証基盤について、開発を担当しているPham Hoangです。本記事では、Global KINTO ID Platform (GKIDP) に実装されたパスキーについてお話します。 OpenID Summit Tokyo 2024 に参加して、OIDC と組み合わされたパスキーについて伺ってから、パスキーが私たちのIDプラットフォームにどれだけ多くの利益をもたらすかついて、お伝えしたいと思いました。 I. GKIDP でのパスキーの自動入力 パスキーは、パスワードの代替となるもので、ユーザーの端末からより速く、より簡単に、より安全に、ウェブサイトやアプリへサインインすることができます。以下は、ユーザーがワンクリックでパスキー認証を行う方法です。 ![](/assets/blog/authors/pham.hoang/fig1.gif =400x) 図1.KINTO ItalyのIDプラットフォームへパスキーでログインする様子 パスキーの素晴らしいところはシームレスなUXで、パス ワード の自動入力機能と同じです。ユーザーはパスキーとパスワードの複雑な違いを理解する必要はありません。このシステムは、ユーザーが覚えておく必要のあるパスワードなどを使わずに、裏側で非対称暗号化を使用します。FaceID認証だけで、すべての設定が完了します。 パスキーは、2022年後半からAndroidとiOSによってサポートされている、最も安全で最先端の認証システムです。まだ開発中で、現在もアップグレードされ続けています。GKIDP (Global KINTO ID Platform)に最新技術で便利な状態を保つため、2023年7月にパスキーの自動入力機能を導入しました。この導入は、メルカリ、ヤフージャパン、GitHubやMoneyForwardでそれぞれ導入したすぐあとのことです。 次のパートでは、パスキーをFederated Login(連携ログイン)に活用し、GKIDPユーザーが「グローバルログイン」機能をより快適に利用できるようにする方法について説明します。 II. Federated Identityにおけるパスキー Global KINTO ID Platform (GKIDP) は、2024年3月時点でイタリア、ブラジル、タイ、カタールと南米各国に導入されているKINTOサービスの認証システムです。GDPRおよびその他のデータ保護規制に遵守するため、GKIDPは各国ごとに複数のIDプロバイダー(IDP)に分けられており、「コーディネーター」を通してユーザーを一つのグローバルIDとして識別します。グローバルID を活用することで、ユーザーは世界中のKINTOサービスを共通のIDで利用することができます。 図2.GKIDP とパスキー対応のIDP 通常、パスキーでログイン(図1を参照)をすると、ユーザーはローカルIDPを使用して認証連携を行い、自国内のKINTOサービスを利用できます。しかし、私たちの場合、RP(Relying Party)のアプリケーションまたはブラジルの KINTO ONE Personal やその他のKINTOサービスのような「サテライトサービス」でパスキー機能が使えないといけないため、各国のIDP (例:ブラジルIDP)にパスキーを実装しました。 この利点について、私たちが参加した OpenID Summit Tokyo 2024 でも取り上げられており、パスキーをOpenID Connectプロトコルと組み合わせて実装することが推奨されていると知れて良かったです。 さらにGKIDPには、KINTOサービスがある他国にユーザーが旅行や引越しをした際、自国サービスと同様に国外サービスでもKINTOまたは関連サービスにログインできる独自の機能があります。これを私たちは「グローバルログイン」機能と呼んでいます。利用には複数のステップが必要ですが、1つのユーザー名とパスワードで管理できるので、サービスごとにユーザー名とパスワードを覚えなくても良くなります。さらにパスキー実装によって、ログイン情報を覚えたり入力したりする必要なく、簡単な手順でグローバルユーザーのログインプロセスの無駄をなくします。例えば、イタリアのKINTO GOユーザー(図1のユーザー)が、グローバルログインを利用してタイのKINTO SHAREサービスにアクセスする方法を見てみましょう。わずか数回のクリックでログイン時間を平均2~3分から約30秒に短縮することができています(図3)。ローカルIDPがパスキーをサポートしているかどうかに関係なく、1つのパスキーを使用してすべてのKINTOサービスにアクセスできます。 ![](/assets/blog/authors/pham.hoang/fig3.gif =300x) 図3. パスキーによるグローバルログイン パスキーは、ローカルログインとグローバルログインだけでなく、再認証などを含むすべての認証画面にも活用されています。一度パスキーが登録されると、ユーザーは何かを確認するためのパスワードをもはやほとんど必要としません。 III. パスキーとその需要 図4. パスキー登録ユーザー イタリアのIDPでは、875名のユーザーパスキーを利用して登録しており、パスキーリリース後の新規ユーザーの52.2%を占めています。パスキーの自動入力をサポートするOSにアップデートするユーザーが増えるにつれてにつれて、この割合も増えることを期待しています。(iOS 16.0以上、Android 9以上) デスクトップユーザーが多くを占めるKINTO Brazilでは、Microsoft PCでパスキーが広く利用されていないにも関わらず、リリース後の新規登録ユーザー1176人のうち20%以上がパスキーを使用しています。 IV. さいごに KINTOのエンジニアとして、パスワードレスの未来のために新しい技術を導入し、ユーザーのデータ保護を強化できることをとても嬉しく思います。パスキーを活用することで、ユーザーは最高レベルのセキュリティで簡単にログインできるようになりました。これからも、世界中のKINTOサービスを新しく我々のIDPハブ:GKIDPに繋ぐことができるのを楽しみにしています。 Hoang Phamの他の記事はこちら: https://blog.kinto-technologies.com/posts/2022-12-02-load-balancing/
[[[Amazonへのリンク]]]( https://amzn.asia/d/06GXK0Fd ) ハンス・P・バッハー、サナタン・スルヤヴァンシ共著 『Vision』の内容を忘れないよう備忘録としてまとめようと考えておりましたが、とても良い本なので共有したいと思い、ここにその一部を紹介いたします。 日常に溢れるデザインされたビジュアルは、私たちに様々な感情を呼び起こします。なぜ特定のビジュアルが私たちに強い印象を与えるのか、またその背後にある心理をどのように理解するかを、この本は解き明かしてくれます。 著者はビジュアルを通じてストーリーを語るための具体的な方法、例えば色彩や形の選択が感情にどのように作用するかを教えてくれます。これにより、専門家でなくとも日々の視覚的体験を豊かに解釈できるようになると思います。 『Vision』を読むことで、私達の日常に新たな視点が生まれると思います。このブログを通じて興味を持たれた方は、ぜひ手に取ってみることをお勧めします。 こちらの書籍は以下の内容で構成されています。 序文 はじめに ビジュアルコミニュケーションのプロセスとは 画像の心理学 ライン シェイプ 明度 色 光 カメラ 構図 まとめ 今回はこの中で「ビジュアルコミニュケーションのプロセスとは」「画像の心理学」「ライン」の内容をかいつまんで紹介していこうと思います。 ビジュアルコミニュケーションのプロセスとは ビジュアルコミニュケーションのプロセスとは、目から入ったものが瞬時に様々な感情を引き起こす自動的処理だと著者は言っています。 例えば、「薄暗い路地に伸びる影」「そこで恐怖におののく人」が描かれた映画のポスターを見るだけで、私達はその映画が不安や恐怖をテーマにしているのだと直感的に認識します。この瞬間的な感情の反応は自動的に引き起こされているものなのだということです。 この本の目的は、読者がこのような自動的処理をプロセスや要素に分解し、なぜそういった気持ちが引き起こされるのかを理解できる力をつけることだと述べられており、早速次の章ではこの自動処理を心理的側面から説明してくれます。 画像の心理学 画像を見て、リラックスしたり恐怖を感じたりするのはなぜか。このプロセスを説明するにあたり画像が及ぼす心理学的側面の三要素について言及しています。 ①関連付け ②メカニズム ③響くとき ①関連付け 例えば、薄暗い路地裏と暗い影が組み合わさると、恐怖を感じることが一般的です。このように、画像や映像は私たちの過去の記憶にリンクしており、脳はこれらを見ると自動的に特定の感情を想起させるそうです。これは「連想」のプロセスに似ています。 したがって、適切なビジュアル要素を選択し関連付けることで、作品は見る人に強烈な印象を与えることができまのだといいます。 ②メカニズム 視覚デザインにおいて、ライン、シェイプ、色といった要素の組み合わせは重要な役割を果たします。例えば対立色※1が隣り合わせに配置されると対比が生じて刺激を生み出します。この様に視覚要素が相互作用して時に刺激や調和を生じさせるということです。 ③響くとき 「言わんとすることが「響く」のは伝えようとする内容とその伝え方が一致したときだ。」(引用 p20) 例えば大切な人の悲痛なる死を語る場面にポップなカラーリングを使用した場合、その悲しみは伝わりづらくなるといった具合に、内容と伝え方が一致してないものは見ている人の心に響かないということです。 こうした色彩などのデザイン要素を積極的に組み合わせることで、『絵』の魅力が向上すると、著者は強調しています。さらに、そうした要素を「偶然」や「あるがまま」に任せるべきではなく、意図的に選択することによって見る人の感情に訴えかけるべきだと述べています。 画像のアナトミー アナトミーとは「解剖学」のことです。 以下に列挙した項目を使って「絵」を分解していくことで「見方」を構築していくことが可能になるといい、それがビジュアルでストーリーを語るための基本だと著者は述べています。そしていつでも見返すことが出来るようにしておくことをお勧めしています。 被写体 文字通り被写体のこと。 フォーマット: 画像の縦横比。 向き: 縦長もしくは横長。 フレーミング: 構図内の配置。 ライン: 線状の要素。 シェイプ: フレーム内の形状。 明度(バリュー): 明るさまたは暗さの度合い。 色: 文字通り色のこと。 パターン: デザインまたは繰り返しの要素。 シルエット: デザイン要素の輪郭内を黒く塗りつぶしたもの。 テクスチャ: デザイン要素の輪郭を示す情報。 光: 明るく輝く要素。 奥行き: 空間の感覚。 エッジ: シェイプを隔てる境界の強弱。 動き: すべての動く要素。 ライン ラインは「構図線」、「コンポジショナルライン」と呼ばれ、視線がたどる経路を作り出します。基本すぎて軽視されがちですが、多様な側面を持ち様々な演出を可能にする力を持つと著者は述べています。下図は主なラインの例の図解(一部抜粋)となっています。 フレームの境界線。1~4 全てに該当。どの構図にも必ず存在する上下左右の枠線のことです。 1・2:構図内の人物が、その方向に応じて構図線になっています。 3:オブジェクトの実際の動きおよび暗示された動きが、明確なラインを形成しています。 4:暗い塊が、構図線になっています。 ラインの方向 ラインの方向とはフレーム上下左右の枠線に対するラインの位置関係のことです。ラインの方向で感情を表現することが可能で、適切なモチーフと組み合わせることで豊かな感情を表すことができます。 例) 垂直:重力に抗う強さ、気品(木や建物など頭上高くそびえるもの) 斜め:水平垂直に対するコントラストにより、ドラマ、エネルギー、ダイナミックさ(崩れたバランスと動感) 水平:穏やか、静か (水平線、海、開けた場所) ラインの配置 ラインの配置によってフレームが分割され、シェイプが生み出されます。そのシェイプのバランスによって構図の魅力が変化します。 均等分割、左右対称:非自然的、人工的。 非対称:バランス次第で魅力的になる。三分割、黄金比など。 ラインの質 ラインの質や特徴は感情を強く喚起します。 直線:緊張感 曲線:ソフト感 太線:力強さや頑丈さ 極細線:洗練、繊細さ 調和と対比 フレーム内にラインを描いた途端に、調和か対比が生み出されます。つまりライン同士の関係がリズム、調和、不調和、バランス、アンバランス、統一などを生み出すということです。 例えば下端に水平なラインは調和を生み出すが、それを斜めにすることによって途端に対比が生じることになる。しかし調和も対比も行き過ぎると退屈さや煩雑さにつながるのでバランスには注意が必要だということです。 リズム ラインを繰り返すことによってリズムが生じ、構図に新たな側面が加わります。 一定間隔で規則的なライン:整然さ、(退屈さ) ランダムな繰り返し:エネルギッシュ、緊張感 【まとめ】 適切に関連付けされたデザイン要素を使用することにより上手くメカニズムが働き見る人の心に響くビジュアルになる。たとえシンプルなラインという要素であっても感情や緊張感、退屈さ、調和、対比といった演出が可能だということです。 さらに著者が繰り返しているのは、「ディテールにとらわれず、単純化して考える。」ということです。それを繰り返すうちに構図作りに対する理解が深まり、自分なりに応用を利かすことができるようになるはずだ、と述べています。 以上、序盤を一部をご紹介するという形で書かせていただきました。ご紹介した部分だけでもビジュアルの分析について視野が広がると感じていただけるのではないでしょうか。 また機会がありましたら他の章もご紹介できたらと思います。
[[[Amazonへのリンク]]]( https://amzn.asia/d/06GXK0Fd ) ハンス・P・バッハー、サナタン・スルヤヴァンシ共著 『Vision』の内容を忘れないよう備忘録としてまとめようと考えておりましたが、とても良い本なので共有したいと思い、ここにその一部を紹介いたします。 日常に溢れるデザインされたビジュアルは、私たちに様々な感情を呼び起こします。なぜ特定のビジュアルが私たちに強い印象を与えるのか、またその背後にある心理をどのように理解するかを、この本は解き明かしてくれます。 著者はビジュアルを通じてストーリーを語るための具体的な方法、例えば色彩や形の選択が感情にどのように作用するかを教えてくれます。これにより、専門家でなくとも日々の視覚的体験を豊かに解釈できるようになると思います。 『Vision』を読むことで、私達の日常に新たな視点が生まれると思います。このブログを通じて興味を持たれた方は、ぜひ手に取ってみることをお勧めします。 こちらの書籍は以下の内容で構成されています。 序文 はじめに ビジュアルコミニュケーションのプロセスとは 画像の心理学 ライン シェイプ 明度 色 光 カメラ 構図 まとめ 今回はこの中で「ビジュアルコミニュケーションのプロセスとは」「画像の心理学」「ライン」の内容をかいつまんで紹介していこうと思います。 ビジュアルコミニュケーションのプロセスとは ビジュアルコミニュケーションのプロセスとは、目から入ったものが瞬時に様々な感情を引き起こす自動的処理だと著者は言っています。 例えば、「薄暗い路地に伸びる影」「そこで恐怖におののく人」が描かれた映画のポスターを見るだけで、私達はその映画が不安や恐怖をテーマにしているのだと直感的に認識します。この瞬間的な感情の反応は自動的に引き起こされているものなのだということです。 この本の目的は、読者がこのような自動的処理をプロセスや要素に分解し、なぜそういった気持ちが引き起こされるのかを理解できる力をつけることだと述べられており、早速次の章ではこの自動処理を心理的側面から説明してくれます。 画像の心理学 画像を見て、リラックスしたり恐怖を感じたりするのはなぜか。このプロセスを説明するにあたり画像が及ぼす心理学的側面の三要素について言及しています。 ①関連付け ②メカニズム ③響くとき ①関連付け 例えば、薄暗い路地裏と暗い影が組み合わさると、恐怖を感じることが一般的です。このように、画像や映像は私たちの過去の記憶にリンクしており、脳はこれらを見ると自動的に特定の感情を想起させるそうです。これは「連想」のプロセスに似ています。 したがって、適切なビジュアル要素を選択し関連付けることで、作品は見る人に強烈な印象を与えることができたのだといいます。 ②メカニズム 視覚デザインにおいて、ライン、シェイプ、色といった要素の組み合わせは重要な役割を果たします。例えば対立色※1が隣り合わせに配置されると対比が生じて刺激を生み出します。この様に視覚要素が相互作用して時に刺激や調和を生じさせるということです。 ③響くとき 「言わんとすることが「響く」のは伝えようとする内容とその伝え方が一致したときだ。」(引用 p20) 例えば大切な人の悲痛なる死を語る場面にポップなカラーリングを使用した場合、その悲しみは伝わりづらくなるといった具合に、内容と伝え方が一致してないものは見ている人の心に響かないということです。 こうした色彩などのデザイン要素を積極的に組み合わせることで、『絵』の魅力が向上すると、著者は強調しています。さらに、そうした要素を「偶然」や「あるがまま」に任せるべきではなく、意図的に選択することによって見る人の感情に訴えかけるべきだと述べています。 画像のアナトミー アナトミーとは「解剖学」のことです。 以下に列挙した項目を使って「絵」を分解していくことで「見方」を構築していくことが可能になるといい、それがビジュアルでストーリーを語るための基本だと著者は述べています。そしていつでも見返すことが出来るようにしておくことをお勧めしています。 被写体 文字通り被写体のこと。 フォーマット: 画像の縦横比。 向き: 縦長もしくは横長。 フレーミング: 構図内の配置。 ライン: 線状の要素。 シェイプ: フレーム内の形状。 明度(バリュー): 明るさまたは暗さの度合い。 色: 文字通り色のこと。 パターン: デザインまたは繰り返しの要素。 シルエット: デザイン要素の輪郭内を黒く塗りつぶしたもの。 テクスチャ: デザイン要素の輪郭を示す情報。 光: 明るく輝く要素。 奥行き: 空間の感覚。 エッジ: シェイプを隔てる境界の強弱。 動き: すべての動く要素。 ライン ラインは「構図線」、「コンポジショナルライン」と呼ばれ、視線がたどる経路を作り出します。基本すぎて軽視されがちですが、多様な側面を持ち様々な演出を可能にする力を持つと著者は述べています。下図は主なラインの例の図解(一部抜粋)となっています。 フレームの境界線。1~4 全てに該当。どの構図にも必ず存在する上下左右の枠線のことです。 1・2:構図内の人物が、その方向に応じて構図線になっています。 3:オブジェクトの実際の動きおよび暗示された動きが、明確なラインを形成しています。 4:暗い塊が、構図線になっています。 ラインの方向 ラインの方向とはフレーム上下左右の枠線に対するラインの位置関係のことです。ラインの方向で感情を表現することが可能で、適切なモチーフと組み合わせることで豊かな感情を表すことができます。 例) 垂直:重力に抗う強さ、気品(木や建物など頭上高くそびえるもの) 斜め:水平垂直に対するコントラストにより、ドラマ、エネルギー、ダイナミックさ(崩れたバランスと動感) 水平:穏やか、静か (水平線、海、開けた場所) ラインの配置 ラインの配置によってフレームが分割され、シェイプが生み出されます。そのシェイプのバランスによって構図の魅力が変化します。 均等分割、左右対称:非自然的、人工的。 非対称:バランス次第で魅力的になる。三分割、黄金比など。 ラインの質 ラインの質や特徴は感情を強く喚起します。 直線:緊張感 曲線:ソフト感 太線:力強さや頑丈さ 極細線:洗練、繊細さ 調和と対比 フレーム内にラインを描いた途端に、調和か対比が生み出されます。つまりライン同士の関係がリズム、調和、不調和、バランス、アンバランス、統一などを生み出すということです。 例えば下端に水平なラインは調和を生み出すが、それを斜めにすることによって途端に対比が生じることになる。しかし調和も対比も行き過ぎると退屈さや煩雑さにつながるのでバランスには注意が必要だということです。 リズム ラインを繰り返すことによってリズムが生じ、構図に新たな側面が加わります。 一定間隔で規則的なライン:整然さ、(退屈さ) ランダムな繰り返し:エネルギッシュ、緊張感 【まとめ】 適切に関連付けされたデザイン要素を使用することにより上手くメカニズムが働き見る人の心に響くビジュアルになる。たとえシンプルなラインという要素であっても感情や緊張感、退屈さ、調和、対比といった演出が可能だということです。 さらに著者が繰り返しているのは、「ディテールにとらわれず、単純化して考える。」ということです。それを繰り返すうちに構図作りに対する理解が深まり、自分なりに応用を利かすことができるようになるはずだ、と述べています。 以上、序盤を一部をご紹介するという形で書かせていただきました。ご紹介した部分だけでもビジュアルの分析について視野が広がると感じていただけるのではないでしょうか。 また機会がありましたら他の章もご紹介できたらと思います。
はじめに こんにちは!iOSエンジニアのViacheslav Voronaです。チームメンバーと一緒に今年開催のtry! Swift Tokyoに参加したことで、Swiftコミュニティ全体の動向について考えることができました。かなり新しいものもあれば、前々からあったけれど最近になって進化したものもあり、本記事では私の所感を皆さんにお伝えします。 見て見ぬふりはできない話題... まずは避けて通れないこの話題から。待望のApple Vision Proが発売されたのは、try! Swift開催のおよそ2ヵ月前でした。try! Swiftの会場がAppleファンで溢れていたのにも納得いきます。Apple Vision Proをまだ試着したことの無い人たちは、「数分だけでも装着してみたい!」と、そのチャンスを切望していました。 Satoshi Hattori氏による「SwiftでvisionOSのアプリをつくろう」の会場は満席でした。アプリ自体は、ユーザーの仮想空間に 円形のタイマー を浮かべるだけのシンプルなものでしたが、服部さんが実際にヘッドセットを装着し、リアルタイムでワークの結果を見せ始めると、会場は大きく盛り上がりました。 また、本カンファレンスの2日目には空間コンピューティングのファンたちが小さな非公式ミーティングを開いていました。Appleの他のデバイスとは異なり、Vision ProはSwiftコミュニティ内で、独自のサブコミュニティを形成しています。映画で近未来的な仮想デバイスを見て育った人たちは、サイバーパンクの夢に近づいていることを実感し始めているのです。エキサイティングである反面、人によっては脅威に感じるかもしれません。 そしてもちろん、カンファレンスのオープニングでの「Swift Punk」のパフォーマンスもVision Proにインスパイアされたものだということは忘れずに触れておきます。 $10000+の小道具で行われたオープニングパフォーマンス Swiftの新境地 最先端のトレンドではなくても、最近は多方面において興味深い開発が進められています。つまり、Swiftコミュニティが、Appleデバイスの領域を超えてさらに拡大しようとしているということです。 サーバーサイドSwiftなどは以前から存在しています。 Vapor は2016年にリリースされ、広く採用されたわけではないですが、今も稼働し続けています。Vapor Core Teamの Tim Condon 氏により、try! Swiftで大規模なコードベースの移行について大変興味深いプレゼンを聞くことができました。これはVaporがversion5.0でSwift Concurrencyを完全にサポートするために現在進めている移行に大きく影響されています。Tim氏によると、そのバージョンは2024年夏にリリースされる予定なので、サーバーサイドSwiftを試してみたい方にとっては始めるのに絶好のタイミングかもしれません。 Vaporの仕掛け人、Tim Condon氏。シャツが良い感じ! Swiftで書かれたAPIに合わせて、同じSwift言語を使ってWebページを実装してみることもできます。これは Paul Hudson 氏のトークテーマでした。Swiftリザルトビルダーを利用したHTML生成に関するPaul氏の講演は、経験豊かな彼だからこそできるもので、とてもおもしろかったです。スピーチのクライマックスは、彼がスピーチで話していたのとまったく同じ原理を使った新しいサイトビルダー、 Ignite の発表でした。 Paul Hudson氏: Igniteも含め多くのものを裏で支えている仕掛け人 このカテゴリーでもう一つ印象的だったのは、クロスプラットフォームSwiftをこよなく愛する Saleem Abdulrasool 氏によるもので、WindowsとmacOSの違いと類似点、そしてSwift開発者がWindowsアプリケーションを作ろうとする際に直面する課題について話してくれました。 最後に忘れてはいけないのが、 Yuta Saito 氏によるSwiftのバイナリ削減ストラテジーについてです。一見、私が本記事で書いているトレンドとは関係無いように見えますが、齋藤さんが Playdate という小さなゲーム機にデプロイされたシンプルなSwiftアプリを見せたときに、無関係ではないことに気づきました。感動的でした。 SwiftがAppleのプラットフォームで新しい機能を得るだけでなく、新しい領域も絶えず探求しているのは喜ばしいことです。 "ザ・コンピューター (パラノイア)" 最後に、ここ数年あちこちで話題となり、新しい「なによりも強力な」モデルが次々と出てくるAIやLLMなどのトピックについてお話します。デジタル・ゴールドラッシュの昨今、ソフトウェア企業はAI処理をありとあらゆるものに適用しようとしています。もちろん、Swiftコミュニティもその影響を受けずにはいられません。try! Swiftでも、この傾向が随所に見られました。 カンファレンスで最初に行われたプレゼンの一つは、Duolingoのエンジニアである Xingyu Wang 氏によるものでした。OpenAIと共同で導入したロールプレイ機能について、AIを搭載したバックエンドの活用、AI生成にかかる時間を最適化するための挑戦、そしてそれを軽減するためにXingyu氏のチームが適用したソリューションについて語られました。全体的に前向きで、AIが秘める無限の可能性を明るいイメージで描かれていたのを覚えています。 一方で、カンファレンスの前に私が注目したのは、 Emad Ghorbaninia 氏による "AIがない未来を考える / What Can We Do Without AI in the Future?"のセッションです。どんな内容なのか、とても興味を持っていました。実際に聴講して、AIのさらなる発展に伴い、開発者として、そして人間として、私たちが今後直面するであろう課題について深く考えさせられました。Emad氏の考えによると人工知能に対抗するためには、人間が最もその強みを出せる創造的なプロセスに焦点を当てるべき、とのことでした。反論できません。 さいごに try! Swift Tokyoでのディスカッションをふり返り、Swiftコミュニティの進化や最新の技術動向に適応していっている様子は非常に興味深いです。Apple Vision Proのような革新的なハードウェアを取り入れることから、サーバーサイドSwiftやAIの統合といった新たな領域の開拓まで、今回見えた進展は技術の動向に広く敏感に対応するコミュニティの姿勢を浮き彫りにしています。この好奇心とイノベーションへの情熱が、SwiftをiOS開発に限定された言語ではなく、ソフトウェアの可能性を広げるための強力なツールセットにしています。今後も、開発者の創造性と技術のダイナミックな相互作用はSwiftコミュニティ内でさらにエキサイティングな進歩をもたらすことが期待されます。この活気に満ちたエコシステムの一員となれることは非常に楽しみです!
はじめに こんにちは!KINTOテクノロジーズでiOSアプリケーションを開発しているFelixです。Swiftに焦点を当てたカンファレンスに行くのは初めてでした。2024年3月22日から24日まで、渋谷で開催されたtry! Swift 2024 Tokyoに参加しました。業界の最新トレンドに触れ、他のエンジニアとのネットワークを広げる絶好の機会となりました。 プレゼン いろいろな説得力のあるプレゼンの中で、特に印象に残ったものを2つ挙げさせてください。 まず、DuolingoのAIチューター機能についてのプレゼンです。講演者のXingyu Wangさんは、AIチューター機能の実装に関して講演されました。また、チャットインターフェイスの構築や、有益なフレーズのレイテンシーの最適化といった課題に触れ、GPT-4の機能を活用した解決策を紹介しました。フロントエンドだけでなく、現在直面している課題にも言及しながら、ソフトウェア全体のアーキテクチャーについてお話しいただけて非常に良かったです。個人的な話ですが、以前私は「日本人ユーザー向けの英語学習アプリを開発する」という、同じような目標を持っていました。この知識は、同じようなサービスを作る上で非常に有用です。よくできたロールプレイ機能を組み込むことで、学習者が実生活に近い環境で会話スキルを練習することができるようになります。 もう一つご紹介したいのは、フレームワークのコミュニティで有名なPoint-Freeによるものです。Swiftのversion 5.9で導入されたSwiftマクロテストに関する発表が特に印象的でした。コンパイラプラグインであるマクロは、新しいコードや診断、修正を生成することで、Swiftのコードを強化します。プレゼンターのお二人は、Swiftの微細なニュアンスを強調し、これらのマクロを作成することやテストすることの複雑さを紹介くださいました。また、彼らのテストライブラリであるswift-macro-testingが、マクロのテストプロセスをより簡素化し、効率的かつ効果的にすることで、Appleのツールを向上させる方法についても示してくださいました。プレゼンターの方々がSwiftを深く理解した上で開発ワークフローの改善に向けて革新的なアプローチを取っているかがわかりました。 ブース ブースエリアは、企業と交流したり、ノベルティを集める参加者でにぎわっていました。サイバーエージェントのブースは特に魅力的で、参加者がポストイットにコードの要約を書き込めるホワイトボードが設置されていました。このインタラクティブなブース企画は、知識を深めるのに役立ったのと同時に、Swiftへの関心をさらに高めるのに効果的だと思いました。 今回のカンファレンスでは、通常の質疑応答ではなく、プレゼンのあとに質問がある人は指定されたブースで登壇者と直接会って話すことができる、という新しいスタイルが採られていました。これより、参加者がより質問しやすくなり、登壇者との交流ができるため、より良いコミュニケーションやネットワーキングの機会になったと思います。 ワークショップ カンファレンス最終日には、好きなワークショップを選んで参加することができました。私はTCAに関するワークショップを選び、約200人を収容する大きな部屋の後ろの方に座りました。このワークショップでは、主にコンポーザブル・アーキテクチャーを使用してサンプルの「SyncUp」アプリを開発する方法について解説されていました。私も最初は一緒にコーディングしようとしましたが、最終的には見学することにしました。興味深い点は、このフレームワークが副作用を管理するための構造化されたアプローチを提供していることです。アプリの外部と相互作用する部分がテスト可能で、理解しやすいものとなっています。ユニットテストのプロセスは特に効率的で明確に見えました。 さいごに 今回初めてtry! Swift Tokyoに参加して、非常に充実した良い経験となりました。このカンファレンスは業界のリーダーや仲間とつながるためのプラットフォームとなっており、私は最先端のSwift技術に夢中になりました。プレゼンは有意義で、iOS開発における現実世界の課題と創造的なソリューションについて深く掘り下げた内容が提供されていた印象でした。インタラクティブなブース企画や専門分野に特化したワークショップは、非常に良い学習やネットワーキングの機会となり、このカンファレンスの大きな価値となっていました。最後まで読んでくださり、ありがとうございました!この記事を読んでご興味を持たれた方はぜひ来年のtry! Swiftにぜひご参加ください!
Introduction Hello, Tech Blog readers. We have recently decided to implement Marketing Cloud and to use the " Norikae GO email delivery" in it, considering the creation of a Journey to trigger an automated process instead of sending individual emails. A Journey is a feature that automatically deploys multiple marketing strategies when a customer takes a specific action. For example, when a customer clicks on a specific link in an email, the relevant information is automatically delivered as part of an automated marketing process. Unfortunately, we were having troubles finding a way to add Journey Builder as an activity in Automation Studio. So, I have summarized in this article the results of the various trials we did. Email Delivery Partner There are several reasons for using Journey Builder: ・Can leverage branching, randomness, and engagement ・Can be integrated with Salesforce, for example, when creating tasks and cases, updating objects, etc. However, Journey Builder does not allow you to execute scripts or SQL queries. For example, you need to merge synced data sources before sending a large volume of emails. In such cases, Journey Builder must be called after these activities are completed in Automation Studio. Therefore, it is desirable to integrate Automation Studio with Journey Builder to send emails. Let's see how to set this up together. Settings Create an Automation, and add the Schedule as the starting source. Configure the Schedule to the future time and save it. Remember to save it, otherwise later settings will not work. Add your desired activity, such as SQL queries and filters. This is essential for integrating with Journey. Journey cannot be triggered if no data extension is selected. Create a Journey. Add a data extension as the entry source. Select the data extension used in Step 2. This is important. If you choose a different data extension, you will not be able to integrate with Automation in Step 1. Note: At this point, even if you save the journey and return to Automation, you will not be able to select the journey from the activities. This is because there is no "Journey" option for Automation activities. But, wait a moment. Now, I'm going to show you some magic! ![Step3-2](/assets/blog/authors/Robb/20240319/03-2.png =300x) In Journey, click "Schedule" at the bottom of the canvas, select "Automation" as the schedule type, and then click "Select." Can't choose "Automation" because it is inactive? Why don't you go back to Step 1 and save Automation? In "Schedule Summary," click "Schedule Settings" and select the Automation you created in Step 1. Edit a contact's rating to specify the records to be processed by Journey. Add email, and flow control, etc. Your setup is now complete! Let’s validate and activate the journey. Don't worry, emails will not be sent immediately after activation, as the timing of the transmission depends on Automation. Back to the Automation, now the Journey was added to Automation on its own, right? Don't you think it's amazing? Finally, summon the courage to activate your Automation. See, every time Automation is triggered, Journey will also be triggered! Thank you for reading. Here, I am going to take a break with a cup of coffee. I hope you will all refresh yourselves with your favorite drink and enjoy the automatic email delivery. Happy marketing! Source: https://www.softwebsolutions.com/resources/salesforce-integration-with-marketing-automation.html
Introduction Hello everyone! I am Kin-chan from the KINTO Technologies' Development Support Division. I usually work as a corporate engineer, maintaining and managing IT systems used throughout the company. The other day, I presented the "Study session in the format of case presentations + roundtable discussions, specialized in the corporate IT domain" at the event " KINTO Technologies MeetUp!" 4 case studies for information systems shared by information systems - " In this article, I will introduce the content of the case study presented at that study session, along with supplementary information. The Presentation You can check below for the full presentation material (in Japanese): [An Introduction to AGILE SaaS] The Secrets to Achieving Maximum Results Quickly with Minimum Workload In addition to the slides I used in my presentation, I will provide additional information to clarify any difficult parts and cover topics I couldn't address at the event. Title Selection First of all, I'd like you to examine the title. Many people interpret "Agile" in different ways, making it daunting to include in the title of a presentation. However, I chose to have it anyway because I hope that someone who listened to or saw my presentation might gain insights like "Oh, so this can also be considered Agile" or "It's not such a difficult topic," and inspire them to take new actions. (Of course, the fact that it's an "attractive" keyword was also a factor in my decision.) What I Will vs. Will Not Speak About Today Since I used the keyword "Agile" in the title, I thought it would be good to focus on content that can be linked to the value of Agile software development. If you're interested in hearing more detailed information about processes or the small stories that occurred during projects, please consider joining KINTO Technologies. Background The introduction of IT Service Management (ITSM) tools, which include inquiry and request management, began with the IT team. Due to its relatively smooth implementation, there was a basis for extending it to management departments beyond IT. Before this flow was established, there wasn’t many opportunities to interact with "other managing departments beyond IT" within the company. Personally, I had previous project experiences with many non-IT departments, including before my previous job. So, when I was appointed to drive this project, I felt glad because I thought I could leverage my past experiences. The decision to opt for an Agile approach stemmed from the background of having a rough goal in mind but not having concrete set of requirements or functions established, and wanting to achieve success with minimal workload while still creating value. Instead of a rigidly defined phased implementation (as one would do in a Waterfall model), the Agile approach, which involves iterating through dialogue and course corrections while building minimal viable products, seemed more suitable. I have this slide here that says, "I think it's better to go Agile!" It might seem like we had already decided on Agile from the project's inception, but in reality, it was more like, "Hmm, how should we move forward? Let's start by listening to what the stakeholders have to say." It was after conducting hearings with the Administration Department team members that we gained a sense of, "With them, we could proceed with this style!" which led us to adopt the Agile approach mentioned later. About Agile When someone asks me "What is Agile?" within the company, I typically respond with something like, "It's a state where work progresses by focusing on value while iterating Kaizen (continuous improvement) in short cycles." While those familiar with software development may understand the values and principles outlined in the Agile Manifesto, others might not resonate with it. Lately, I've noticed that explaining Agile has become easier with the publication of books like 'The Agile Kata' and other Agile books targeting non-IT audiences. As for the progress of the project... For the next slides, I made a conscious effort to explain "What makes it Agile?" in a way that links back to the values outlined in the Agile Manifesto as much as possible. The message I wanted to convey with this slide is the establishment of mechanisms to minimize unnecessary communication and facilitate immediate engagement in essential conversations. In typical software development scenarios, one common question might be, "What tasks are currently being performed?" and for clarifying "What do we want to accomplish?" . Given that this is a "SaaS implementation with a certain degree of framework already established," I deemed it more appropriate to explore "effective usage based on the existing framework" rather than "defining requirements based solely on current tasks." Furthermore, one of the strengths of low-code tools is their significantly lower cost for the build-break-fix process in the initial stages. This made it feasible to create a prototype providing minimal value before the first meeting. As a result, instead of starting the conversation with "So, what kind of product do you want to create?" during the initial meeting, we were able to begin with discussions focused on specific functional prototypes, asking questions like "How about a system that works like this? Do you notice any issues with it?" This allowed us to engage in discussions centered around tangible, functional examples right from the start. These aspects focus on the following values in the Manifesto for Agile Software Development: Individuals and interactions over processes and tools Working software over comprehensive documentation What I wanted to convey in this slide is “to create value at short intervals, get feedback, and create a mechanism to provide a system that makes sense”. One common aspect in meetings is taking points as homework for internal discussion later. For example, "consider what kind of menu structure is good" or "discuss internally what kind of process flow is best". But this time, rather than leaving such "takeaway considerations" entirely to the other party, we opted to participate in these discussions by being invited as guests to their alignment sessions. By doing so, we can immediately address any questions, concerns, or discrepancies that arise during the conversation, and we can swiftly provide answers or even start making system adjustments on the spot. As a result, despite being in a "separate discussion" setting, we were able to progress not only with specification changes based on the discussions but also with actual functional improvements. Furthermore, I mentioned here that "significant specification changes emerged at this point," but what I meant was that we were able to detect a situation where it was more beneficial to essentially "start over" rather than modify what has been done so far. Of course, this meant discarding what has been built till that point. However, by actively participating in the discussions, we were able to fully understand the necessity and value of rebuilding. This allowed us to make this decision with confidence. These aspects focus on the following values in the Manifesto for Agile Software Development: Customer collaboration over contract negotiation Responding to change over following a plan Finishing the project Through this project, one of the most significant gains I feel I've obtained is "trust." It's just my unilateral opinion, but I feel that I've contributed to creating opportunities where people think, "Working with this person leads to good results," and "I'd like to consult with them again if there's something next time." Certainly, I believe there are many approaches to project management that can yield positive results, not just those aligned with Agile practices like the example we discussed. But If you ever find yourself stuck on how to proceed, I recommend considering the values inherent in Agile as a reference and trying to adjust your actions just a little: Envision your desired outcomes and apply small changes to achieve them. Observe the results of those small changes in behavior and use that feedback to further refine your vision of the desired outcome. Continue to make further small changes in your behavior. Once you're able to repeat this process, it's safe to say you've adopted an 'Agile' mindset. Conclusion As mentioned at the beginning, I hope to inspire anyone who has gone through this material to gain insights such as "Oh, this is also Agile" or "It's not such a difficult topic". I would be happy if this can serve as encouragement for your next actions.
Introduction Greetings, this is Morino from KINTO Technologies. On June 29th (Thursday) to the 30th (Friday) in 2023, I attended with a colleague the Cyber Security Symposium Dogo 2023 held in Matsuyama City, Ehime Prefecture. The purpose of the event is to recognize the importance of countermeasures against cyberattacks as digitalization accelerates with the development of society that coexists with the coronavirus, and to fight cyberattacks with the power of local security. The purpose of the seminar was to deepen discussions on policy trends, technological trends, and examples of cyber attacks. We were able to get a lot of inspiration and knowledge from the lectures and other participants. When I arrived at Matsuyama Airport, we were greeted by Mican, a mascot promoting the image of Ehime Prefecture. There was also a mikan (mandarin orange) juice tower and a mikan juice faucet. There were many interesting talks and presentations at the symposium, but I would like to introduce some of the ones that left an impression on me. (See a full list of talks and presentations here .) Japan's Cybersecurity Policy First of all, Mr. Tomoo Yamauchi (Director-General, Cybersecurity Office, Ministry of Internal Affairs and Communications) gave a keynote speech on "Japan's Cybersecurity Policy." Under the theme of "leaving no one behind," Mr. Yamauchi explained the country's efforts to secure a free, fair and safe cyberspace. This included changes in targets during Cybersecurity Awareness Month, and improvements in cloud usage within government agencies, etc. I felt that the theme of “leaving no one behind” was wonderful. Security (Security + Community) and Generated AI As for the night session, I listened to a lecture on "Security (Security + Community) and Generated AI" by Mr. Tsuneyoshi Hamamoto (IT Integration Department, Energia Communications, Inc.) and Mr. Matcha Daifuku (Risk Consulting Department, luck Technology, Inc.). Mr. Hamamoto explained the concept of secuminity , a term coined by combining security with community. Secuminity is where people concerned with security interact, share knowledge and experiences, as well as collaborate and learn from each other online and offline. I understood it to be a community that contributes to improving security. Next, he shared his knowledge on Generative AI. The presentation materials are available here (in Japanese). From a security perspective, while we had expectations for its use in detecting suspicious activity from logs, we were also concerned about its use in generating sophisticated phishing emails. Student Research Award Winning Research Presentation Finally, on the second day, outstanding students presented their research findings at the Student Research Award Presentation. I voted for the presentation titled "Proposal of KP-less Method for Individual Cyber Exercises Based on Tabletop Role-Playing Games (TRPG)" as it was the one I found most compelling in the symposium, whereas participants voted for the best presentation. This presentation was made by Ms. Erika Fujimoto (Graduate School of Regional Design and Development, University of Nagasaki), who proposed an exercise method for individuals based on TRPG (Tabletop Role-Playing Games) in “KP-less” style as a cyber exercise scenario. “KP-less” means that there is no one in the TRPG to take on the role of the Game Master, the organizer. I was drawn to it due to my ongoing interest in information security education as a security officer. When I was in elementary school and junior high school, game books became very popular. So I understood that it was an exercise incorporating that method. Summary These were some of the talks and presentations at the Cybersecurity Symposium Dogo 2023. There were many other useful lectures and presentations. The symposium was a valuable opportunity not only to learn about the latest insights on cybersecurity, but also to interact with people who are interested in the same field. I want to thank the organizers, sponsors, and attendees.
Introduction Hello! Thank you for reading! My name is Nakamoto and I develop the front end of KINTO FACTORY ('FACTORY' in this article), a service that allows you to upgrade your current car. In this article, I would like to introduce a method of how to detect errors that occur in clients such as browsers using AWS CloudWatch RUM. Getting Started The reason why we introduced it was due to an enquiry we received by our Customer Center (CC), where a user tried to order products from the FACTORY website, only to encounter an error where the screen did not transition. This prompted an investigation request. I immediately parsed the API log and checked if there were any errors, but I could not find anything that would lead to an error. Next, I checked what kind of model and browser was being used to access the front end. When examining the access logs from Cloud Front, I looked into the access of the relevant user and checked the User-Agent where I could see: Android 10; Chrome/80.0.3987.149 It was accessed from a relatively old Android device. With that in mind, while analyzing the source of the page where the problem occurred, a front end development team member advised that replaceAll in JavaScript might be the culprit... This function requires compatibility with Chrome version 85 or higher... (Since FACTORY recommends using the latest version of each browser, we hadn't tested cases with old versions such as this case in QA.) *Other members of the team also told me that you can easily search for functions here to see which browsers and versions are supported! Until now, monitoring in FACTORY has detected errors in the BFF layer and notified PagerDuty and Slack, but it has not been possible to detect errors in the client-side, so it was the first time we noticed them through communication from customers. If we continued as-is, we would not be able to notice such errors on the client side unless we received customer feedback, so we decided to take countermeasures. Detection Method Originally, FACTORY's frontend had been loading client.js from AWS's CloudWatch RUM (Real-time User Monitoring). However, this function was not being used for anything in particular (user journeys, etc. are analyzed separately with Google Analytics), so it was a bit of a waste. As I investigated, I learned that RUM allows JavaScript to send events to CloudWatch on a client such as a browser. So using this mechanism, I decided to create a system to send and detect custom events when some kind of error occurs. Notification Method The general flow of notifications are as follows: When an error is detected in the browser, CloudWatch RUM sends a custom event with the error description in the message window.crm("recordEvent", { type: "error_handle_event", data: { /* Information required for analysis. The contents of the exception error */ }, }); Cloud Watch Alerm detects the above events and sends the error details via SNS when the event occurs The above SNS notifies SQS, Lambda picks up the message and notifies the error to OpenSearch (this mechanism uses the existing API error detection and notification mechanism) After Implementation After implementing this mechanism in the production environment and operating it for several months, I can luckily say that critical issues, such as the JavaScript error that resulted in its introduction, have not occurred. However, I have been able to detect cases where errors occur due to unintended access from search engine crawlers and bots, and I have become aware of accesses that I did not pay particular attention to until I introduced it, so it became a reminder of the importance of monitoring and being vigilant. Conclusion In order to enable the best online purchase experiences on websites such as FACTORY, it's very important to prevent as many errors as possible (such as problems when buying items, viewing pages, etc.). However, there is unfortunately a limit as to how much we can guarantee that it works on all customers' devices and browsers. That is why, if an error occurs, it is necessary to show easy to understand messages for the customers (with what they should do next), and a mechanism in place for us, the developers on the operation side, so that we can quickly identify the occurrence and details of the problem. I would like to continue using different tools and mechanisms to ensure stable website operation.
はじめに こんにちは!KTCグローバル開発部に所属している崔です。 現在 KINTO FACTORY の開発に参加しており、今年はチームメンバーと一緒にWebサービス内のメモリリークの原因を調査し、特定した問題点を修正して解決しました。 このブログでは、調査アプローチ、使用したツール、調査結果、そしてメモリリークに対処するための措置について詳しく説明します。 背景 私たちが現在開発・運用しているKINTO FACTORYサイトには、AWSのECS上で動作しているWebサービスがあります。 このサービスでは、当社が開発・運営している認証サービスである会員PF(Platform)と決済サービスである決済PF(Platform)を利用しています。 今年1月に、このWebサービスでECSタスクのCPU使用率が異常に高まり、一時的にサービスにアクセスできない事態が発生しました。 この際、KINTO FACTORYサイトで特定の画面遷移や操作を行うと404エラーやエラーダイアログが表示されるインシデントが発生しました。 昨年7月にも類似のメモリリークが発生しており、Full GC(Old領域のクリア)が頻繁に発生し、それに伴うCPU使用率の増加が原因であることが判明しました。 これらの事象が発生した場合、一時対策としてECSタスクの再起動で解決できますが、メモリリークの根本原因を究明し、解決する必要があります。 本記事では、これらの事例を踏まえ、現象の調査・分析とそれに基づいた解決策を記載しています。 調査内容と結果の要約 調査内容 最初に、本件で発生した事象の詳細を分析すると、WebサービスのCPU使用率が異常に高まるのは、Full GC(Old領域のクリア)が頻繁に発生することで起きた問題あることが分かりました。 通常、Full GCが一度行われると、多くのメモリが解放され、しばらくの間は再度発生することはありません。 にもかかわらず、Full GCが頻繁に発生するのは、使用中のメモリが過剰に消費されている可能性が高く、これはメモリリークが発生していることを示唆しています。 この仮説を検証するために、メモリリークが発生した期間中に多く呼ばれたAPIを中心に長時間APIを呼び出し続け、 メモリリークを再現させました。その後、メモリの状況やダンプを分析して原因を探ります。 調査に使用したツールは以下の通りです: JMeter でのAPIのトラフィックシミュレーション VisualVM と Grafana を用いたメモリ状態の監視(ローカル環境および検証環境) OpenSearch で頻繁に呼び出されるAPIのフィルタリング また、本文によく現れているメモリの「Old領域」について以下の通りに簡単に説明します: Javaのメモリ管理では、ヒープ領域がYoung領域とOld領域に分かれています。 Young領域には新しく作成されたオブジェクトが格納され、ここで一定期間存続したオブジェクトはSurvivor領域を経てOld領域に移動します。 Old領域には長期間存続するオブジェクトが格納され、ここがいっぱいになるとFull GCが発生します。 Survivor領域はYoung領域内の一部で、オブジェクトがどれだけ長く生存しているかを追跡します。 調査結果 外部サービスのリクエスト時に接続インスタンスが大量に新規作成されており、メモリが無駄に占有されていることによるメモリリークが発生していました。 調査内容の詳細 1. 呼び出し回数が多かったAPIの洗い出し 最初に、多く呼ばれている処理とメモリ使用状況を知るため、OpenSearchでAPI呼び出しサマリのダッシュボードを作成しました。 2. 洗い出ししたAPIをローカル環境で30分間呼び出し続け、結果を分析 調査方法 メモリリークをローカル環境で再現させ、ダンプを取り原因分析を行うため、以下の設定でJMeterを使用してAPIを30分間呼び出し続けました。 JMeterの設定 スレッド数:100 Ramp-up期間(※):300秒 テスト環境 Mac OS Javaバージョン:openjdk 17.0.7 2023-04-18 LTS Java設定:-Xms1024m -Xmx3072m ※Ramp-up期間とは:設定したスレッド数を何秒以内に起動・実行するかの指定される秒数です。 結果と仮説 メモリリークは起きませんでした。実際の環境と異なるためメモリリークが再現しなかったと考えました。実際の環境はDockerで動作しているため、アプリケーションをDockerコンテナに入れて再度検証することにしました。 3. Docker環境で再度APIを呼び出し続け、結果を分析 調査方法 メモリリークをローカル環境で再現させるため、以下の設定でJMeterを使用してAPIを1時間呼び出し続けました。 JMeterの設定 スレッド数:100 Ramp-up期間:300秒 テスト環境 ローカルDockerコンテナ(Mac上) メモリ制限:4 GB CPU制限:4コア 結果 ローカル環境で環境を変えてもメモリリークは起きませんでした。 仮説 実際の環境と異なる 外部APIを呼び出していない 長時間にわたるAPI呼び出しで少しずつメモリが蓄積される可能性がある 大きすぎるオブジェクトがSurvivorに入らず、Old領域に入ってしまう可能性がある やはりローカル環境では再現できないため、本番環境に近い検証環境で再度検証することにしました。 4. 検証環境で外部API関連を長時間叩き続け、結果を分析 調査方法 メモリリークを検証環境で再現させるため、以下の設定でJMeterを使用してAPIを呼び出し続けました。 呼び出し対象API:それぞれ計7本 継続期間:5時間 ユーザー数:2 ループ:200(1000を予定していたが、実際のOrderは少ないため200に変更) Factory API合計呼び出し回数:4000 影響がある外部PF:会員PF(1600回)、決済PF(200回) 結果 Full GCが発生せず、メモリリーク現象は再現しませんでした。 仮説 ループ回数が少なく、メモリ使用量が増加しているが上限に達していないためFull GCが発動されなかった。呼び出し数を増やし、メモリ上限を下げてFull GCを発生させるようにします。 5. メモリ上限を下げ、APIを長時間叩き続ける 調査方法 検証環境でメモリ上限を下げて、JMeterで会員PF関連APIを4時間呼び出し続けました。 時間:4時間 API:前回と同じ7つのAPI 頻度:12ループ/分(5秒/ループ) 会員PF呼び出し頻度:84回/分 4時間の会員PF呼び出し回数:20164回 ダンプ取得設定: export APPLICATION_JAVA_DUMP_OPTIONS='-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/log/app/ -XX:OnOutOfMemoryError="stop-java %p;" -XX:OnError="stop-java %p;" -XX:ErrorFile=/var/log/app/hs_err_%p.log -Xlog:gc*=info:file=/var/log/app/gc_%t.log:time,uptime,level,tags:filecount=5,filesize=10m' ECSのメモリ上限設定: export APPLICATION_JAVA_TOOL_OPTIONS='-Xms512m -Xmx512m -XX:MaxMetaspaceSize=256m -XX:MetaspaceSize=256m -Xss1024k -XX:MaxDirectMemorySize=32m -XX:-UseCodeCacheFlushing -XX:InitialCodeCacheSize=128m -XX:ReservedCodeCacheSize=128m --illegal-access=deny' 結果 メモリリークの再現に成功し、ダンプを取得できました。 IntelliJ IDEAでダンプファイルを開くと、メモリの詳細情報を見ることができます。 ダンプファイルを詳しく分析したところ、外部API関連部分でリクエストごとに大量のオブジェクトが新規作成されていること、Util系クラスの一部がSingletonとして扱われていないことが判明しました。 6. Heap Dumpの分析結果 reactor.netty.http.HttpResources 内に HashMap$Node が5,410個作成されており、352,963,672バイト(83.09%)を専有していることが分かりました。 メモリリーク発生箇所特定 reactor.netty.resources.PooledConnectionProvider 内の channelPools(ConcurrentHashMap) でリークが発生しており、格納と取得のロジックに着目しました。 poolFactory(InstrumentedPool) 取得箇所 remote(Supplier<? extends SocketAddress>) と config(HttpClientConfig) から取得した channelHash で holder(PoolKey) を作成 holder(PoolKey) で channelPools から poolFactory(InstrumentedPool) を取得し、同様のキーが存在すれば返し、なければ新規作成 リークの原因は、同一設定でも同一キーと判断されないことです: reactor.netty.resources.PooledConnectionProvider public abstract class PooledConnectionProvider<T extends Connection> implements ConnectionProvider { ... @Override public final Mono<? extends Connection> acquire( TransportConfig config, ConnectionObserver connectionObserver, @Nullable Supplier<? extends SocketAddress> remote, @Nullable AddressResolverGroup<?> resolverGroup) { ... return Mono.create(sink -> { SocketAddress remoteAddress = Objects.requireNonNull(remote.get(), "Remote Address supplier returned null"); PoolKey holder = new PoolKey(remoteAddress, config.channelHash()); PoolFactory<T> poolFactory = poolFactory(remoteAddress); InstrumentedPool<T> pool = MapUtils.computeIfAbsent(channelPools, holder, poolKey -> { if (log.isDebugEnabled()) { log.debug("Creating a new [{}] client pool [{}] for [{}]", name, poolFactory, remoteAddress); } InstrumentedPool<T> newPool = createPool(config, poolFactory, remoteAddress, resolverGroup); ... return newPool; }); channelPoolsは名称の通りChannel情報を保持しているオブジェクトで同様のリクエストが来た際に再利用を行っている。 PoolKeyはホスト名と接続設定のHashCodeを元に作成され、更にそのHashCodeが使用される。 channelHash 取得箇所 reactor.netty.http.client.HttpClientConfig の階層 Object + TransportConfig + ClientTransportConfig + HttpClientConfig PooledConnectionProviderに渡されるLambda式 com.kinto_jp.factory.common.adapter.HttpSupport L5 ここで定義されたLambda式が PooledConnectionProvider に config#doOnChannelInit として引き渡される。 abstract class HttpSupport { ... private fun httpClient(connTimeout: Int, readTimeout: Int) = HttpClient.create() .proxyWithSystemProperties() .doOnChannelInit { _, channel, _ -> channel.config().connectTimeoutMillis = connTimeout } .responseTimeout(Duration.ofMillis(readTimeout.toLong())) ... } 7. channelPools取得時の挙動(図解) キーが一致するケース(正常) channelPools に存在する情報がキーとなり、 InstrumentedPool が再利用される。 キーが不一致のケース(正常) channelPools に存在しない情報がキーとなり、 InstrumentedPool が新規作成される。 今回発生したケース(異常) channelPools に存在する情報がキーとなるが、 InstrumentedPool が再利用されず新規作成されてしまう。 問題箇所の修正と検証 修正箇所 問題となっているLambda式をプロパティ呼び出しに書き換える 修正前 abstract class HttpSupport { ... private fun httpClient(connTimeout: Int, readTimeout: Int) = HttpClient.create() .proxyWithSystemProperties() .doOnChannelInit { _, channel, _ -> channel.config().connectTimeoutMillis = connTimeout } .responseTimeout(Duration.ofMillis(readTimeout.toLong())) ... } 修正後 abstract class HttpSupport { ... private fun httpClient(connTimeout: Int, readTimeout: Int) = HttpClient.create() .proxyWithSystemProperties() .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, connTimeout) .responseTimeout(Duration.ofMillis(readTimeout.toLong())) ... } 検証 前提条件 MembersHttpSupport#members(memberId: String) を1000回呼び出す。 PooledConnectionProvider#channelPools に格納されているオブジェクトの件数を確認する。 修正前の結果 修正前の状態で実行したところ、 PooledConnectionProvider#channelPools に1000個のオブジェクトが格納されていることが分かりました(リークの原因)。 修正後の結果 修正後の状態で実行したところ、 PooledConnectionProvider#channelPools に1個のオブジェクトが格納されていることが分かりました(リーク解消)。 まとめ 今回の調査では、KINTO FACTORYのWebサービスにおけるメモリリークの原因を特定し、適切な修正を行うことで問題を解決することができました。特に、外部API呼び出し時に大量のオブジェクトが新規作成されていたことがメモリリークの原因であると判明し、Lambda式をプロパティ呼び出しに変更することで解消されました。 このプロジェクトを通じて、以下の重要な教訓を得ることができました: 持続的なモニタリング :ECSサービスのCPU使用率の異常やFull GCの頻繁な発生を通じて、継続的なモニタリングの重要性を認識しました。システムのパフォーマンスを常に監視することで、問題の兆候を早期に察知し、迅速に対処することができます。 早期の問題特定と対策 :Webサービスのメモリリークを疑い、長時間APIを呼び出してメモリ状況を再現させることで、外部サービスのリクエスト時に大量のオブジェクトが新規作成されていることを特定しました。これにより、問題の原因を迅速に特定し、適切な修正を実施できました。 チームワークの重要性 :複雑な問題に対処する際には、チーム全員が協力して取り組むことが成功への鍵となります。今回の修正と検証は、開発チーム全員の協力と努力によって達成されました。特に、調査、分析、修正、検証といった各ステップでの協力が成果を上げました。 調査フェーズでは、多くの苦労がありました。例えば、メモリリークの再現がローカル環境では難しく、実際の環境に近い検証環境で再度検証を行う必要がありました。また、外部APIを長時間にわたって呼び出し続けることで、メモリリークを再現し、その原因を特定するのに多くの時間と労力を要しました。しかし、これらの困難を乗り越えることで、最終的には問題を解決することができ、大きな達成感を得ることができました。 この記事を通じて、システムのパフォーマンス向上と安定性を維持するための実践的なアプローチや教訓を共有しました。同様の問題に直面している開発者の方々の参考になれば幸いです。 以上です〜
はじめに こんにちは!KTCグローバル開発部に所属している崔です。 現在 KINTO FACTORY の開発に参加しており、今年はチームメンバーと一緒にWebサービス内のメモリリークの原因を調査し、特定した問題点を修正して解決しました。 このブログでは、調査アプローチ、使用したツール、調査結果、そしてメモリリークに対処するための措置について詳しく説明します。 背景 私たちが現在開発・運用しているKINTO FACTORYサイトには、AWSのECS上で動作しているWebサービスがあります。 このサービスでは、当社が開発・運営している認証サービスである会員PF(Platform)と決済サービスである決済PF(Platform)を利用しています。 今年1月に、このWebサービスでECSタスクのCPU使用率が異常に高まり、一時的にサービスにアクセスできない事態が発生しました。 この際、KINTO FACTORYサイトで特定の画面遷移や操作を行うと404エラーやエラーダイアログが表示されるインシデントが発生しました。 昨年7月にも類似のメモリリークが発生しており、Full GC(Old領域のクリア)が頻繁に発生し、それに伴うCPU使用率の増加が原因であることが判明しました。 これらの事象が発生した場合、一時対策としてECSタスクの再起動で解決できますが、メモリリークの根本原因を究明し、解決する必要があります。 本記事では、これらの事例を踏まえ、現象の調査・分析とそれに基づいた解決策を記載しています。 調査内容と結果の要約 調査内容 最初に、本件で発生した事象の詳細を分析すると、WebサービスのCPU使用率が異常に高まるのは、Full GC(Old領域のクリア)が頻繁に発生することで起きた問題あることが分かりました。 通常、Full GCが一度行われると、多くのメモリが解放され、しばらくの間は再度発生することはありません。 にもかかわらず、Full GCが頻繁に発生するのは、使用中のメモリが過剰に消費されている可能性が高く、これはメモリリークが発生していることを示唆しています。 この仮説を検証するために、メモリリークが発生した期間中に多く呼ばれたAPIを中心に長時間APIを呼び出し続け、 メモリリークを再現させました。その後、メモリの状況やダンプを分析して原因を探ります。 調査に使用したツールは以下の通りです: JMeter でのAPIのトラフィックシミュレーション VisualVM と Grafana を用いたメモリ状態の監視(ローカル環境および検証環境) OpenSearch で頻繁に呼び出されるAPIのフィルタリング また、本文によく現れているメモリの「Old領域」について以下の通りに簡単に説明します: Javaのメモリ管理では、ヒープ領域がYoung領域とOld領域に分かれています。 Young領域には新しく作成されたオブジェクトが格納され、ここで一定期間存続したオブジェクトはSurvivor領域を経てOld領域に移動します。 Old領域には長期間存続するオブジェクトが格納され、ここがいっぱいになるとFull GCが発生します。 Survivor領域はYoung領域内の一部で、オブジェクトがどれだけ長く生存しているかを追跡します。 調査結果 外部サービスのリクエスト時に接続インスタンスが大量に新規作成されており、メモリが無駄に占有されていることによるメモリリークが発生していました。 調査内容の詳細 1. 呼び出し回数が多かったAPIの洗い出し 最初に、多く呼ばれている処理とメモリ使用状況を知るため、OpenSearchでAPI呼び出しサマリのダッシュボードを作成しました。 2. 洗い出ししたAPIをローカル環境で30分間呼び出し続け、結果を分析 調査方法 メモリリークをローカル環境で再現させ、ダンプを取り原因分析を行うため、以下の設定でJMeterを使用してAPIを30分間呼び出し続けました。 JMeterの設定 スレッド数:100 Ramp-up期間(※):300秒 テスト環境 Mac OS Javaバージョン:openjdk 17.0.7 2023-04-18 LTS Java設定:-Xms1024m -Xmx3072m ※Ramp-up期間とは:設定したスレッド数を何秒以内に起動・実行するかの指定される秒数です。 結果と仮説 メモリリークは起きませんでした。実際の環境と異なるためメモリリークが再現しなかったと考えました。実際の環境はDockerで動作しているため、アプリケーションをDockerコンテナに入れて再度検証することにしました。 3. Docker環境で再度APIを呼び出し続け、結果を分析 調査方法 メモリリークをローカル環境で再現させるため、以下の設定でJMeterを使用してAPIを1時間呼び出し続けました。 JMeterの設定 スレッド数:100 Ramp-up期間:300秒 テスト環境 ローカルDockerコンテナ(Mac上) メモリ制限:4 GB CPU制限:4コア 結果 ローカル環境で環境を変えてもメモリリークは起きませんでした。 仮説 実際の環境と異なる 外部APIを呼び出していない 長時間にわたるAPI呼び出しで少しずつメモリが蓄積される可能性がある 大きすぎるオブジェクトがSurvivorに入らず、Old領域に入ってしまう可能性がある やはりローカル環境では再現できないため、本番環境に近い検証環境で再度検証することにしました。 4. 検証環境で外部API関連を長時間叩き続け、結果を分析 調査方法 メモリリークを検証環境で再現させるため、以下の設定でJMeterを使用してAPIを呼び出し続けました。 呼び出し対象API:それぞれ計7本 継続期間:5時間 ユーザー数:2 ループ:200(1000を予定していたが、実際のOrderは少ないため200に変更) Factory API合計呼び出し回数:4000 影響がある外部PF:会員PF(1600回)、決済PF(200回) 結果 Full GCが発生せず、メモリリーク現象は再現しませんでした。 仮説 ループ回数が少なく、メモリ使用量が増加しているが上限に達していないためFull GCが発動されなかった。呼び出し数を増やし、メモリ上限を下げてFull GCを発生させるようにします。 5. メモリ上限を下げ、APIを長時間叩き続ける 調査方法 検証環境でメモリ上限を下げて、JMeterで会員PF関連APIを4時間呼び出し続けました。 時間:4時間 API:前回と同じ7つのAPI 頻度:12ループ/分(5秒/ループ) 会員PF呼び出し頻度:84回/分 4時間の会員PF呼び出し回数:20164回 ダンプ取得設定: export APPLICATION_JAVA_DUMP_OPTIONS='-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/log/app/ -XX:OnOutOfMemoryError="stop-java %p;" -XX:OnError="stop-java %p;" -XX:ErrorFile=/var/log/app/hs_err_%p.log -Xlog:gc*=info:file=/var/log/app/gc_%t.log:time,uptime,level,tags:filecount=5,filesize=10m' ECSのメモリ上限設定: export APPLICATION_JAVA_TOOL_OPTIONS='-Xms512m -Xmx512m -XX:MaxMetaspaceSize=256m -XX:MetaspaceSize=256m -Xss1024k -XX:MaxDirectMemorySize=32m -XX:-UseCodeCacheFlushing -XX:InitialCodeCacheSize=128m -XX:ReservedCodeCacheSize=128m --illegal-access=deny' 結果 メモリリークの再現に成功し、ダンプを取得できました。 IntelliJ IDEAでダンプファイルを開くと、メモリの詳細情報を見ることができます。 ダンプファイルを詳しく分析したところ、外部API関連部分でリクエストごとに大量のオブジェクトが新規作成されていること、Util系クラスの一部がSingletonとして扱われていないことが判明しました。 6. Heap Dumpの分析結果 reactor.netty.http.HttpResources 内に HashMap$Node が5,410個作成されており、352,963,672バイト(83.09%)を専有していることが分かりました。 メモリリーク発生箇所特定 reactor.netty.resources.PooledConnectionProvider 内の channelPools(ConcurrentHashMap) でリークが発生しており、格納と取得のロジックに着目しました。 poolFactory(InstrumentedPool) 取得箇所 remote(Supplier<? extends SocketAddress>) と config(HttpClientConfig) から取得した channelHash で holder(PoolKey) を作成 holder(PoolKey) で channelPools から poolFactory(InstrumentedPool) を取得し、同様のキーが存在すれば返し、なければ新規作成 リークの原因は、同一設定でも同一キーと判断されないことです: reactor.netty.resources.PooledConnectionProvider public abstract class PooledConnectionProvider<T extends Connection> implements ConnectionProvider { ... @Override public final Mono<? extends Connection> acquire( TransportConfig config, ConnectionObserver connectionObserver, @Nullable Supplier<? extends SocketAddress> remote, @Nullable AddressResolverGroup<?> resolverGroup) { ... return Mono.create(sink -> { SocketAddress remoteAddress = Objects.requireNonNull(remote.get(), "Remote Address supplier returned null"); PoolKey holder = new PoolKey(remoteAddress, config.channelHash()); PoolFactory<T> poolFactory = poolFactory(remoteAddress); InstrumentedPool<T> pool = MapUtils.computeIfAbsent(channelPools, holder, poolKey -> { if (log.isDebugEnabled()) { log.debug("Creating a new [{}] client pool [{}] for [{}]", name, poolFactory, remoteAddress); } InstrumentedPool<T> newPool = createPool(config, poolFactory, remoteAddress, resolverGroup); ... return newPool; }); channelPoolsは名称の通りChannel情報を保持しているオブジェクトで同様のリクエストが来た際に再利用を行っている。 PoolKeyはホスト名と接続設定のHashCodeを元に作成され、更にそのHashCodeが使用される。 channelHash 取得箇所 reactor.netty.http.client.HttpClientConfig の階層 Object + TransportConfig + ClientTransportConfig + HttpClientConfig PooledConnectionProviderに渡されるLambda式 com.kinto_jp.factory.common.adapter.HttpSupport L5 ここで定義されたLambda式が PooledConnectionProvider に config#doOnChannelInit として引き渡される。 abstract class HttpSupport { ... private fun httpClient(connTimeout: Int, readTimeout: Int) = HttpClient.create() .proxyWithSystemProperties() .doOnChannelInit { _, channel, _ -> channel.config().connectTimeoutMillis = connTimeout } .responseTimeout(Duration.ofMillis(readTimeout.toLong())) ... } 7. channelPools取得時の挙動(図解) キーが一致するケース(正常) channelPools に存在する情報がキーとなり、 InstrumentedPool が再利用される。 キーが不一致のケース(正常) channelPools に存在しない情報がキーとなり、 InstrumentedPool が新規作成される。 今回発生したケース(異常) channelPools に存在する情報がキーとなるが、 InstrumentedPool が再利用されず新規作成されてしまう。 問題箇所の修正と検証 修正箇所 問題となっているLambda式をプロパティ呼び出しに書き換える 修正前 abstract class HttpSupport { ... private fun httpClient(connTimeout: Int, readTimeout: Int) = HttpClient.create() .proxyWithSystemProperties() .doOnChannelInit { _, channel, _ -> channel.config().connectTimeoutMillis = connTimeout } .responseTimeout(Duration.ofMillis(readTimeout.toLong())) ... } 修正後 abstract class HttpSupport { ... private fun httpClient(connTimeout: Int, readTimeout: Int) = HttpClient.create() .proxyWithSystemProperties() .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, connTimeout) .responseTimeout(Duration.ofMillis(readTimeout.toLong())) ... } 検証 前提条件 MembersHttpSupport#members(memberId: String) を1000回呼び出す。 PooledConnectionProvider#channelPools に格納されているオブジェクトの件数を確認する。 修正前の結果 修正前の状態で実行したところ、 PooledConnectionProvider#channelPools に1000個のオブジェクトが格納されていることが分かりました(リークの原因)。 修正後の結果 修正後の状態で実行したところ、 PooledConnectionProvider#channelPools に1個のオブジェクトが格納されていることが分かりました(リーク解消)。 まとめ 今回の調査では、KINTO FACTORYのWebサービスにおけるメモリリークの原因を特定し、適切な修正を行うことで問題を解決することができました。特に、外部API呼び出し時に大量のオブジェクトが新規作成されていたことがメモリリークの原因であると判明し、Lambda式をプロパティ呼び出しに変更することで解消されました。 このプロジェクトを通じて、以下の重要な教訓を得ることができました: 持続的なモニタリング :ECSサービスのCPU使用率の異常やFull GCの頻繁な発生を通じて、継続的なモニタリングの重要性を認識しました。システムのパフォーマンスを常に監視することで、問題の兆候を早期に察知し、迅速に対処することができます。 早期の問題特定と対策 :Webサービスのメモリリークを疑い、長時間APIを呼び出してメモリ状況を再現させることで、外部サービスのリクエスト時に大量のオブジェクトが新規作成されていることを特定しました。これにより、問題の原因を迅速に特定し、適切な修正を実施できました。 チームワークの重要性 :複雑な問題に対処する際には、チーム全員が協力して取り組むことが成功への鍵となります。今回の修正と検証は、開発チーム全員の協力と努力によって達成されました。特に、調査、分析、修正、検証といった各ステップでの協力が成果を上げました。 調査フェーズでは、多くの苦労がありました。例えば、メモリリークの再現がローカル環境では難しく、実際の環境に近い検証環境で再度検証を行う必要がありました。また、外部APIを長時間にわたって呼び出し続けることで、メモリリークを再現し、その原因を特定するのに多くの時間と労力を要しました。しかし、これらの困難を乗り越えることで、最終的には問題を解決することができ、大きな達成感を得ることができました。 この記事を通じて、システムのパフォーマンス向上と安定性を維持するための実践的なアプローチや教訓を共有しました。同様の問題に直面している開発者の方々の参考になれば幸いです。 以上です〜
To Be Event Staff at try! Swift Tokyo 2024 With my childcare duties now more manageable, I decided to get more involved in activities and signed up for try! Swift Tokyo 2024! When I noticed they were looking for staff for try! Swift Tokyo 2024, I took the leap and submitted my application. To tell the truth, I had never been to try! Swift Tokyo, even as a participant, so I applied without really knowing what the atmosphere of the venue would be like😅 So in this article, I will share my experiences as a staff member at this event. What is try! Swift Tokyo 2024 try! Swift Tokyo 2024, held in March 2024, is a conference for iOS developers in Japan. Since its inception in 2016, it has consistently served as the largest gathering for professionals in the iOS development community. After a long pause due to COVID-19, this year marked its return for the first time in five years. Please visit the official website for more information. In my experience, iOSDC, another event that is also famous for its large iOS conferences, is largely driven by open speaker requests within Japan to shape the event's schedule. On the other hand, try! Swift Tokyo sources proposals internationally and invites renowned engineers from abroad to enrich its schedule, so there were many situations where we needed to communicate in English. Staff Activities This time, on the day, I worked as a staff member on the organizing side. It was my first time working behind the scenes, but it was a very exciting and enjoyable experience. One week before the event, all the staff gathered for a meeting where responsibilities were assigned. I was assigned to manage the venue, and was asked to do the following: Set up the venue Guiding the participants Venue guidance Handing out lunch boxes Collect garbage Venue teardown Other tasks within the venue ![](/assets/blog/authors/HiroyaHinomori/IMG_2773.jpg =400x) I usually spend most of my time writing code, so I was worried about whether my body could handle three days of physical work. However, I found it surprisingly refreshing to be active and interact with people In particular, I enjoyed talking to the attendees during reception and venue guidance. With many speakers and participants from abroad, try! Swift Tokyo needed English communication, which made me very aware of my limited language skills. Given that it was the first one in five years, there were many newcomers, myself included. Despite the occasional uncertainty about how to do things, everyone was able to work together and enjoy the activities, ending our first day successfully. ![](/assets/blog/authors/HiroyaHinomori/IMG_2784.jpg =400x) On the second day during the venue teardown, it was nice to see that some people had left their signatures on the sponsor boards that remained👍 During the after party which followed the teardown, the participants and staff were able to have fun together, and it was very nice to meet new people there. On the third day, a workshop was conducted for participants, and witnessing their enthusiasm was truly inspiring, leaving me feeling uplifted💪 I had some free time too, so I took the opportunity to exchange information with other staff members. The churrasco I ate at the post-teardown celebration was also delicious😋 Conclusion ![](/assets/blog/authors/HiroyaHinomori/IMG_2804.jpg =400x) I wanted to take more pictures, but I regret that I couldn't because I was so focused on work... By joining as a staff member, I was able to encounter new people and experiences that I never would have gotten by joining as a participant, which made me feel a sense of fulfillment. I feel that it was a great experience. I'd love to join as staff again if I get the chance next time! If you are reading this article, I encourage you to challenge yourself and consider being a conference staff member as well! Finally, I'd like to say THANK YOU to all the organizers, speakers, and other participants!!! See you again👍
はじめに こんにちは。KINTOテクノロジーズモバイルアプリケーション開発グループの Rasel です。私は現在、 my route Androidアプリの開発に取り組んでいます。 my route は、外出時に利用するマルチモーダルアプリで、目的地の情報収集、地図上のさまざまな場所の探索、デジタルチケットの購入、予約、乗車料金の支払い処理などを行うことができます。 いまやモバイルアプリは私たちの日常生活に欠かせないものです。我々のようなエンジニアは、AndroidとiOSアプリをそれぞれ別で作成するため、両方のプラットフォームを開発するためにはダブルコストが発生します。これらの開発コストを削減するためにReact Native、Flutterなど、様々なクロスプラットフォームフレームワークが登場しました。 しかし、クロスプラットフォームアプリのパフォーマンスには常に課題があります。ネイティブアプリのようなパフォーマンスではありません。また、プラットフォーム固有の新機能がAndroidやiOSからリリースされると、フレームワーク開発者からサポートを受けなければいけない場合があり、さらに時間がかかります。 そこで Kotlin Multiplatform (KMP) が助けになります。ネイティブアプリ並みのパフォーマンスで、プラットフォーム間で共有するコードを自由に選択できるのです。KMPでは、Androidのネイティブ第一言語であるKotlinでAndroidアプリが開発されていて、完全にネイティブなので、パフォーマンス上の問題はほとんどありません。iOSの部分は Kotlin/Native を使用しており、他のフレームワークと比較して、ネイティブアプリとして開発されたものに近いパフォーマンスがあります。 本記事では、SwiftUIコードをCompose Multiplatformと統合する方法を紹介します。 KMP(モバイルプラットフォームではKMMとしても知られています)では、プラットフォーム間で共有するコードの量と、ネイティブアプリに実装するコードを自由に選択でき、プラットフォームのコードとシームレスに統合されます。以前は、ビジネスロジックのみをプラットフォーム間で共有できましたが、今では UI コードも共有できるようになりました。 Compose Multiplatform においても、 UIコードの共有が可能になりました。下にある以前の記事を読むと、モバイルアプリ開発におけるKotlin MultiplatformとCompose Multiplatformの使用法をよりよく理解できます。 Kotlin Multiplatform Mobile (KMM)を使ったモバイルアプリ開発 Kotlin Multiplatform Mobile(KMM)およびCompose Multiplatformを使用したモバイルアプリケーションの開発 それでは、始めましょう! 概要 我々はUI開発でCompose Multiplatformを使用するKMPを用いてアプリ開発をしています。今回はSwiftUIをCompose Multiplatformに統合する方法を示すため、とてもシンプルなGeminiチャットアプリを使用します。また、チャットでのユーザーのクエリへの返信には、Googleの Gemini Pro APIを使用します。デモすることが目的なので、シンプルにするためにも、テキストメッセージのみが許可されるよう無料版の API を使用します。 Compose と SwiftUI がどのように連携するか まず、最初に大事なことから。Jetbrainの Kotlin Multiplatform Wizard を使用してKMPプロジェクトを作成します。このウィザードには、必要になるKMPの基本的なセットアップと、Compose Multiplatformと、いくつかの初期SwiftUIコードが付属しています。 ![Kotlin MultiplatformWizard](/assets/blog/authors/ahsan_rasel/kmp_wizard.png =450x) Kotlin Multiplatform Mobile pluginをインストールして、 Android Studio IDE を使用し、プロジェクトを作成することもできます。 ComposeとSwiftUIがどのように連携するかをデモしてみます。ComposableコードをiOS に組み込むには、Composableコードを ComposeUIViewController 内にラップする必要があります。 ComposeUIViewController は UIKit から UIViewController の値を返し、その中にComposeコードの組み立てをコンテンツパラメータとして含めることができます。 例: // MainViewController.kt fun ComposeEntryPoint(): UIViewController { return ComposeUIViewController { Column( modifier = Modifier.fillMaxSize(), horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Center ) { Text(text = "Hello from Compose") } } } 次に、この関数を iOS 側から呼び出します。そのためには、SwiftUI のComposeコードを表す構造が必要です。以下のコードは、共有モジュールであるUIViewController コードを SwiftUI ビューに変換します。 // ComposeViewControllerRepresentable.swift struct ComposeViewControllerRepresentable :UIViewControllerRepresentable { func updateUIViewController(_ uiViewController:UIViewControllerType, context:Context) {} func makeUIViewController (context:Context)-> some UIViewController { return MainViewControllerKt.ComposeEntryPoint() } } ここで、 MainViewControllerKt.ComposeEntryPoint() の名前を詳しく見てみましょう。これが Kotlin から生成されたコードになります。そのため、共有モジュール内のファイル名とコードによって異なる場合があります。共有モジュール内のファイル名が Main.ios.kt で、 UIViewController returning function nameが ComposeEntryPoint() の場合、 Main_iosKt.ComposeEntryPoint() のように呼び出す必要があります。そのため、コードによって異なります。 次に、この ComposeViewControllerRepresentable をコード ContentView() の内部からインスタンス化します。これで準備は完了です。 // ContentView.swift struct ContentView:View { var body: some View { composeViewControllerRepresentable () .ignoresSafeArea (.all) } } コードを見てわかるように、このComposeコードは SwiftUI 内のどこでも使用でき、SwiftUI 内で好きなようにサイズを制御できます。UI は次のようになります: ![Hello from Swift](/assets/blog/authors/ahsan_rasel/swiftui_compose_1.png =250x) SwiftUI のコードをCompose内に統合したい場合は、 UIView でラップする必要があります。SwiftUIのコードをKotlinで直接記述することはできないため、Swiftで記述してKotlin関数に渡す必要があります。これを実装するために、 関数 ComposeEntryPoint() に、 UIView タイプの引数を追加してみましょう。 // MainViewController.kt fun ComposeEntryPoint(createUIView: () -> UIView): UIViewController { return ComposeUIViewController { Column( modifier = Modifier.fillMaxSize(), horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Center ) { UIKitView( factory = createUIView, modifier = Modifier.fillMaxWidth().height(500.dp), ) } } } そして CreateUIView を以下のような Swift コードへ渡します。 // ComposeViewControllerRepresentable.swift struct ComposeViewControllerRepresentable : UIViewControllerRepresentable { func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) {} func makeUIViewController(context: Context) -> some UIViewController { return MainViewControllerKt.ComposeEntryPoint(createUIView: { () -> UIView in UIView() }) } } さて、他のViewを追加したい場合は、以下のように親ラッパー UIView を作成してください: // ComposeViewControllerRepresentable.swift private class SwiftUIInUIView<Content: View>: UIView { init(content: Content) { super.init(frame: CGRect()) let hostingController = UIHostingController(rootView: content) hostingController.view.translatesAutoresizingMaskIntoConstraints = false addSubview(hostingController.view) NSLayoutConstraint.activate([ hostingController.view.topAnchor.constraint(equalTo: topAnchor), hostingController.view.leadingAnchor.constraint(equalTo: leadingAnchor), hostingController.view.trailingAnchor.constraint(equalTo: trailingAnchor), hostingController.view.bottomAnchor.constraint(equalTo: bottomAnchor) ]) } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } } 次に、それを ComposeViewControllerRepresentable に追加し、必要に応じてViewを追加します。 // ComposeViewControllerRepresentable.swift func makeUIViewController(context: Context) -> some UIViewController { return MainViewControllerKt.ComposeEntryPoint(createUIView: { () -> UIView in SwiftUIInUIView(content: VStack { Text("Hello from SwiftUI") Image(systemName: "moon.stars") .resizable() .frame(width: 200, height: 200) }) }) } 出力は次のようになります: ![Hello from Swift with Image](/assets/blog/authors/ahsan_rasel/swiftui_compose_2.png =250x) この方法では、共有の合成可能なコードに、好きなだけSwiftUIコードを追加できます。 また、 UIKit コードをCompose内に統合したい場合、中間コードを自分で作成する必要はありません。Compose Multiplatformが提供するComposable関数 UIKitView () を使用して、その中にUIKitコードを直接追加できます。 // MainViewController.kt UIKitView( modifier = Modifier.fillMaxWidth().height(350.dp), factory = { MKMapView() } ) このコードは iOS ネイティブのマップ画面をCompose内に統合します。 Gemni Chatアプリの実装 それでは、ComposeコードをSwiftUI内に統合して、 Gemini Chat アプリの実装を進めましょう。Jetpack Compose の LazyColumn を使用して、基本的なチャット UI を実装します。Compose Multiplatform内にSwiftUIを統合することが主な目的なので、Composeやデータ、ロジック等、他の部分の実装についてはここでは割愛します。Gemini Pro APIを実装するため、我々はKtorネットワーキングライブラリを利用しました。Ktorの実装についての詳細は、 Creating a cross-platform mobile application のページをご覧ください。 このプロジェクトでは、Compose Multiplatformで全てのUIを実装しました。Compose MultiplatformのTextFieldではiOS側でパフォーマンスに問題があるので、iOSアプリの入力フィールドにのみSwiftUIを使用します。 ComposeEntryPoint() 関数の中にComposeコードを入れてみましょう。これらのコードには、TopAppBarを含むチャットUIとメッセージのリストが含まれています。これには、Androidアプリで使用される入力フィールドの条件付き実装もあります。 // MainViewController.kt fun ComposeEntryPoint(): UIViewController = ComposeUIViewController { Column( Modifier .fillMaxSize() .windowInsetsPadding(WindowInsets.systemBars), horizontalAlignment = Alignment.CenterHorizontally ) { ChatApp(displayTextField = false) } } false を displayTextField に渡したので、iOS バージョンのアプリでは Compose 入力フィールドがアクティブになりません。そして、Android側のTextFieldにはパフォーマンスの問題がないため、Android 実装側からComposable関数をこの ChatApp () のComposable関数を呼び出すと、 displayTextField の値は true で返ってきます。(これはAndroid のネイティブ UI コンポーネントです。) それでは、Swift コードに戻ってSwiftUIで入力フィールドを実装します。 // TextInputView.swift struct TextInputView: View { @Binding var inputText: String @FocusState private var isFocused: Bool var body: some View { VStack { Spacer() HStack { TextField("メッセージを入力する...", text: $inputText, axis: .vertical) .focused($isFocused) .lineLimit(3) if (!inputText.isEmpty) { Button { sendMessage(inputText) isFocused = false inputText = "" } label: { Image(systemName: "arrow.up.circle.fill") .tint(Color(red: 0.671, green: 0.365, blue: 0.792)) } } } .padding(15) .background(RoundedRectangle(cornerRadius: 200).fill(.white).opacity(0.95)) .padding(15) } } } そして、 ContentView 構造体に戻り、以下のように修正します: // ContentView.swift struct ContentView: View { @State private var inputText = "" var body: some View { ZStack { Color("TopGradient") .ignoresSafeArea() ComposeViewControllerRepresentable() TextInputView(inputText: $inputText) } .onTapGesture { // Hide keyboard on tap outside of TextField UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil) } } } ここでは ZStack を追加し、その中に TopGradient カラーと、Modifier ignoresSafeArea () を追加して、ステータスバーの色が他の UI の色と一致するようにしました。 次に、共有されたCompose コードのラッパー ComposeViewControllerRepresentable を追加し、メインのチャットUIを実装しました。そして、 TextInputView() というSwiftUIビューも追加しました。これにより、iOSアプリのユーザーにもiOSネイティブコードでスムーズなパフォーマンスが提供することができます。最終的なUIは次のようになります。 Gemini Chat iOS Gemini Chat Android ![Gemini Chat iOS](/assets/blog/authors/ahsan_rasel/swiftui_compose_ios.png =300x) ![Gemini Chat Android](/assets/blog/authors/ahsan_rasel/swiftui_compose_android.png =300x) ここでは、ChatAppのUIコード全体がKMPのCompose MultiplatformでAndroidとiOSの両方に共有され、iOSの入力フィールドのみがSwiftUIにネイティブに統合されています。 このプロジェクトの完全なソースコードは、GitHub で公開リポジトリとして公開されています。 GitHubリポジトリ:Compose MultiplatformにおけるSwiftUI さいごに このように、Kotlin Multiplatformと Compose Multiplatform を使うことで、クロスプラットフォームアプリでのパフォーマンスの問題を解決しながら、ユーザーにネイティブのような操作感と外観を提供できます。また、プラットフォーム間でコードを好きなだけ共有できるため、開発コストも削減できます。Compose Multiplatformでは、デスクトップアプリとコードを共有することもできます。ですから、単一のコードベースをデスクトップアプリだけでなくモバイルプラットフォームでも使用できます。さらに、プラットフォーム間でのコードベース共有を促進するため、Webサポートも進行中です。Kotlin Multiplatform (KMP) のもう1つの大きな利点は、コードを無駄にすることなく、いつでもネイティブ開発に切り替えることができる点です。AS-ISのKMPコード はAndroidネイティブのため、Androidアプリではそのまま利用でき、iOSアプリを別途切り離して開発することができます。また、KMPにすでに実装したものと同じSwiftUIコードを再利用することも可能です。このフレームワークは、高性能のアプリケーションを提供するだけでなく、共有するコードの割合を自由に変更したり、ネイティブ開発にいつでも切り替えたりできます。 本記事はここまでとしますが、KINTOテクノロジーズのテックブログでは今後もおもしろい記事を発信していきます!Happy Coding!
Introduction Hello! My name is Morimoto, and I am a backend engineer at KINTO Technologies. I am part of the KINTO ONE development Group where I primarily use Java for KINTO ONE. But this time, I would like to introduce a study session of GraphQL that we're conducting separately from our regular work. What is GraphQL? GraphQL is a query language. Unlike other languages such as SQL, GraphQL can interact with multiple data sources, not just a specific one. If the schema is defined on the backend side, the frontend side can freely retrieve the items in the object according to the definition. Unlike the REST API, with GraphQL, you have the flexibility to specify what information the frontend wants to return from the backend. There is no need to get unnecessary information, and there is no need to call the API multiple times to get nested objects. Purpose of the Study Group There were two main purposes: To improve our technical skills To interact beyond our respective teams To Improve Technical Skills We wanted to catch up with new information in addition to the technology we use in our daily work, but each team member felt that it was a high hurdle to overcome alone. For example, our lack of language knowledge could be cited as a barrier. The GraphQL tutorial we decided to follow used Typescript. So we had to learn Typescript first before learning GraphQL. The idea was that by supplementing each other with our different knowledge and experiences, we could overcome challenges and make the learning curve less steep. To Interact Beyond Our Respective Teams We also wanted to make it as an opportunity for members -regardless of group, team, project or different ages-, to interact with each other. Many of us were good friends who already knew each other, but we were determined to get along better by getting together on a regular basis. I also thought that the study session would be an opportunity to learn about new aspects of each other. Content Details Why GraphQL? Those who typically implemented APIs on the backend were struggling with the need to create an API every time a requirement came. Of course, there are times that is faster to process data on the server side, but it is troublesome to increase the number of APIs that return information as it is. As for me, ever since I heard that GraphQL was a good solution for this, I wanted to try it out. Some members already had some experience using GraphQL, but they wanted to understand the overall process flow, so we decided to properly study it together. Tutorials Used for the Study Sessions We chose Apollo GraphQL for the GraphQL library, and used the tutorial linked below. GraphQL Tutorials The reason why we chose it is due to the volume of tutorials available and we felt it was a good introduction. In addition, one of the study group members had used Apollo GraphQL in their work, so we knew there was a track record of being used within the company. Summary of the Study Group Date and Time It was held once a week after 6pm when members had time. Members The group consists of eight young members ranging from 25 to 28 years old. Our background and expertise was diverse, each belonging to different domains such as web application frontend, backend, as well as mobile application frontend and backend. What We Did We completed all five chapters of the tutorial, from Lift-off I to V. It describes the basics of implementing GraphQL. How It Was Conducted and What We Arranged We conducted the study group following the below flow: We opted to go through the tutorials in Mokumoku-kai style. We reinforced our learning by doing presentations to each other of the content from the tutorials we reviewed. We started by holding our series of Mokumoku-Kai . Mokumoku-Kai is a study group method where everyone gathers, sharing questions and ideas when needed, but mainly focuses on their own. As mentioned, some had used Apollo GraphQL before, but none had a complete picture of the process. For that reason, we first completed the same tutorials and then discussed and resolved any points that came up. However, some mentioned they were doubtful if they really understood it, and that maybe it was good to refresh concepts first before moving on. So we decided to present each section to one another on a rotating basis. The presentation format required presenters to understand the tutorial perfectly. At the study session, there were moments where, upon reviewing, we found answers to questions in parts where we had been progressing somewhat aimlessly. During the presentation however, we could ask questions, change the source code and try it out, and there were new discoveries that one would not have found on their own. A glimpse at one of our sessions. In the foreground are boxes of sandwiches prepared for the study group. Conclusion First and foremost, we achieved a deep understanding of GraphQL thanks to these sessions. By using our knowledge and experience to complement each other, we were able to proceed faster and more reliably than anyone could on their own. Having study partners also helped us to persevere through moments when we felt like giving up. We aim to continue with the remaining chapters of Apollo GraphQL tutorials and learn more about other technical topics. We even discussed how we would love to create some kind of application in the process. By exploring the languages, frameworks, and architectures each of us is interested in, I hope to keep getting better with my technical capabilities.
Introduction Hello! My name is Morimoto, and I am a backend engineer at KINTO Technologies. I am part of the KINTO ONE development Group where I primarily use Java for KINTO ONE. But this time, I would like to introduce a study session of GraphQL that we're conducting separately from our regular work. What is GraphQL? GraphQL is a query language. Unlike other languages such as SQL, GraphQL can interact with multiple data sources, not just a specific one. If the schema is defined on the backend side, the frontend side can freely retrieve the items in the object according to the definition. Unlike the REST API, with GraphQL, you have the flexibility to specify what information the frontend wants to return from the backend. There is no need to get unnecessary information, and there is no need to call the API multiple times to get nested objects. Purpose of the Study Group There were two main purposes: To improve our technical skills To interact beyond our respective teams To Improve Technical Skills We wanted to catch up with new information in addition to the technology we use in our daily work, but each team member felt that it was a high hurdle to overcome alone. For example, our lack of language knowledge could be cited as a barrier. The GraphQL tutorial we decided to follow used Typescript. So we had to learn Typescript first before learning GraphQL. The idea was that by supplementing each other with our different knowledge and experiences, we could overcome challenges and make the learning curve less steep. To Interact Beyond Our Respective Teams We also wanted to make it as an opportunity for members -regardless of group, team, project or different ages-, to interact with each other. Many of us were good friends who already knew each other, but we were determined to get along better by getting together on a regular basis. I also thought that the study session would be an opportunity to learn about new aspects of each other. Content Details Why GraphQL? Those who typically implemented APIs on the backend were struggling with the need to create an API every time a requirement came. Of course, there are times that is faster to process data on the server side, but it is troublesome to increase the number of APIs that return information as it is. As for me, ever since I heard that GraphQL was a good solution for this, I wanted to try it out. Some members already had some experience using GraphQL, but they wanted to understand the overall process flow, so we decided to properly study it together. Tutorials Used for the Study Sessions We chose Apollo GraphQL for the GraphQL library, and used the tutorial linked below. GraphQL Tutorials The reason why we chose it is due to the volume of tutorials available and we felt it was a good introduction. In addition, one of the study group members had used Apollo GraphQL in their work, so we knew there was a track record of being used within the company. Summary of the Study Group Date and Time It was held once a week after 6pm when members had time. Members The group consists of eight young members ranging from 25 to 28 years old. Our background and expertise was diverse, each belonging to different domains such as web application frontend, backend, as well as mobile application frontend and backend. What We Did We completed all five chapters of the tutorial, from Lift-off I to V. It describes the basics of implementing GraphQL. How It Was Conducted and What We Arranged We conducted the study group following the below flow: We opted to go through the tutorials in Mokumoku-kai style. We reinforced our learning by doing presentations to each other of the content from the tutorials we reviewed. We started by holding our series of Mokumoku-Kai . Mokumoku-Kai is a study group method where everyone gathers, sharing questions and ideas when needed, but mainly focuses on their own. As mentioned, some had used Apollo GraphQL before, but none had a complete picture of the process. For that reason, we first completed the same tutorials and then discussed and resolved any points that came up. However, some mentioned they were doubtful if they really understood it, and that maybe it was good to refresh concepts first before moving on. So we decided to present each section to one another on a rotating basis. The presentation format required presenters to understand the tutorial perfectly. At the study session, there were moments where, upon reviewing, we found answers to questions in parts where we had been progressing somewhat aimlessly. During the presentation however, we could ask questions, change the source code and try it out, and there were new discoveries that one would not have found on their own. A glimpse at one of our sessions. In the foreground are boxes of sandwiches prepared for the study group. Conclusion First and foremost, we achieved a deep understanding of GraphQL thanks to these sessions. By using our knowledge and experience to complement each other, we were able to proceed faster and more reliably than anyone could on their own. Having study partners also helped us to persevere through moments when we felt like giving up. We aim to continue with the remaining chapters of Apollo GraphQL tutorials and learn more about other technical topics. We even discussed how we would love to create some kind of application in the process. By exploring the languages, frameworks, and architectures each of us is interested in, I hope to keep getting better with my technical capabilities.
ごあいさつ 皆さまこんにちは。テックブログチーム改め技術広報グループの森です。 実はこの4月より、テックブログチームは「技術広報グループ」として生まれ変わりました✨ 今後ともよろしくお願いします🙇‍♀️ 技術広報以外のお仕事は別記事で書いておりますので、もしご興味あればぜひご一読ください 👀 KINTOのグローバル展開におけるGDPR等個人データ関連法対応 GDPR対応! Cookie同意ポップアップをグローバルサイトに設置した話 導入 2024年1月31日、KINTOテクノロジーズ(KTC)では初となるの全社オフラインミーティングを開催いたしました🎉 2024年のKick Offという位置づけです。実はこのイベント、完全ボトムアップで企画・運営されました。この大規模ミーティングがどのように作られたか、本記事で裏側をご紹介します。今後のための備忘録のようなものですが、「自社で内製イベントすることになったけどどうしよう!?」という方に少しでも参考になれば何よりです。 本来ならすぐにレポートするところを、私の遅筆により約半年後の記事公開となってしまったこと、お許しください🙇‍♀️ (イベント運営の記事は鮮度が大事なのに… 😭) 企画のきっかけ コロナ禍中に弊社従業員数は爆増し、いまや約350名の社員が所属しています。 この規模になるとやはり横の繋がりや一体感を生み出すことはなかなか難しく、以前よりオフラインイベントやチームビルディングイベントを求める声が多くありました。また、トップ層からのメッセージ発信の場も多くはないので、全体ビジョンの浸透には時間を要していました。 そういった課題を踏まえ、「アフターコロナだし、全社員が集まれる機会があれば少しはこの課題もクリアになるかも」とイベント運営によく携わる3名で企画が始まりました。これが11月初旬のお話。 まずは大枠を 11月に3人で企画を開始したのですが1月開催なので実施まで3ヵ月しか期間がなく、スケジュールはかなりタイトでした。 ラフなスケジュールを以下のように引いて進めることになりました。 まずは開催すること自体に賛同を得るため、企画の大枠を以下のように検討しました。 開催目的 2023年1年の総括と2024年のキックオフ 共通のビジョンを共有すること・他部署間交流による組織の一体感醸成 企画内容 毎月の全社員ミーティング(開発編成本部会)の拡大版 前半はオンライン参加可能(業務内) 懇親会はオフライン参加のみ(業務外) コンテンツ Category Time Contents Note リハ 15:00-16:00​ 会場設営/リハーサル 音響準備や進行の調整など 16:00​-16:30​ 入場開始〜受付​ 参加者の受付 本編 16:30-16:35​ 開場〜オープニング 16:35​-16:40​ 2023年の振り返り​​(副社長) 2023年の振り返りと2024年の展望 をシェア 16:40-17:30​ 2023年の漢字​​ 2022年末にも実施しました。各グループの振り返りコーナー 17:30​-17:40​ 休憩 / プレゼン準備​​​ 17:40-18:35​ K-1グランプリ​​​​ 各部2023年の代表案件をプレゼンし、表彰! 18:35​-18:45​ 休憩​​​​​ 18:45-19:00​​ K-1グランプリ 結果発表​​​​​​ 表彰と受賞者からのコメント 19:00​-19:05​ 総括​と2024年に向けて(社長) 2023年総括と2024年への期待をシェア 懇親会 19:05-19:20​ 写真撮影 / 休憩 / 転換​ 19:20​-20:50​ 懇親会​​ ・乾杯+鏡開き ・ミニゲームも入れて全社交流の時間!​ 20:50-21:00​​ 撤収作業​​​ 21:00完全退出​ 各グループを巻き込め! 大枠が決定したので、全体の人数を把握すべく社内に公示しました。 普段の社内イベントはSlackで全社に向けて一度アナウンスすることが多いのですが、今回はなにせ全社イベント。各グループの協力なくしては統率が取れません🤦‍♀️ そこで、各グループから担当者を立てていただき、各グループの取りまとめをお願いしました。 普段は何度も何度も運営からアナウンスしないとなかなか回収しきれない回答も、各グループ担当者に取りまとめていただいたことで比較的スムーズに、〆切までに回収することができました。各G担当の皆様、本当にありがとうございました!大感謝 😭❤️ ![announce](/assets/blog/authors/M.Mori/20240611/announce.png =500x) 私の部での告知の様子 想像以上のオフライン参加率! 今回のイベントは開発編成本部会、つまり全社員ミーティングという建付けですので、基本は全員参加必須です。 家庭の都合や出張などでどうしてもオンライン参加になる方もいらっしゃいますが、それでも300名規模の会場が必要でした。 オフィス近郊での会場探しはかなり苦戦しましたが、片っ端から検索しては電話を繰り返し、奇跡的に神保町オフィスから徒歩5分の 「神田スクエアホール」 を手配することができました。 ![Hall](/assets/blog/authors/M.Mori/20240611/square_hall.jpg =500x) とってもきれいな会場。神田スクエア様、ありがとうございます。 やむを得ずオンライン参加になった方や英語通訳チャネル(後述)のため、本部会パートはWebinar配信も行いました。配信担当の方々、本当にいつもありがとう😭❤️の気持ちです。 各担当で並行してタスクを遂行! イベントを行う際は運営チームを分けてそれぞれでタスクを動かします。KINTOテクノロジーズのすごいところはアサインしたらそれぞれが自走してくれるところ…!!前のめりに動いてくれたり意見してくれたりするので、非常に助かります。 今回は前述の各G代表者の中から数名を複数の役割に分けてアサインしました。 役割 タスク詳細 統括 全体の取りまとめ、各担当者が困ったときの相談役 司会 イベント全体のファシリテーション、盛り上げ(一番重要!)の施策検討 受付 誘導の流れを検討、案内すべき事項の取りまとめ 通訳 多数所属するNon-Japaneseに向けた通訳用に外部通訳者様との調整担当 今年の漢字 各Gから2023年を表す漢字・2023年の成果と2024年への意気込みを取りまとめ K-1グランプリ 各部の代表案件を取りまとめ 社長・副社長挨拶取りまとめ 社長副社長の伝えたいメッセージとイベント趣旨をすり合わせて資料を作成 懇親会 ケータリングを何にするか+懇親会で何をするかの検討 ノベルティ 全員に配布されるノベルティや景品などの作成 司会 当日の様子はまた別の記事でお伝えできると思いますが、今回は以前からイベントの司会や盛り上げをしてくれていた3名に総合司会をお願いしました。当日のタイムラインに合わせてパートの振り分けであったり、当日の流れを想定して、いつのタイミングでどういったスライドが必要か?どう盛り上げるか?などを考えてくれました。ざっくりタイムラインはあったものの、実際に司会をするにあたって気になるポイントを洗い出したり、スクリプトを作ったり。何の依頼もしていないのに「司会お願いします」と言っただけでここまでやってくれていました。感激😭❤️ ![shinko](/assets/blog/authors/M.Mori/20240611/shikai_shinko.png =500x) 進行中の気になるポイント ![Script](/assets/blog/authors/M.Mori/20240611/shikai_script.png =500x) 司会スクリプト 受付 内部イベントとはいえこれだけ多くの人数が集まるイベントとなると、手際のよい受付が非常に重要です。受付担当としてメインで5名が手を挙げてくれました。(そして当日はたくさんの人がお手伝いしてくださいました…!!!) 受付で重要なのはなんといってもいかにスムーズに案内するか!受付でイベント参加者の第一印象が決まるため、受付に人が滞留すればするほどイベントへの不満はたまっていきます。 そこで今回工夫したのは従来の出席者リストで〇xをつけるのではなく、出席者の主体性に任せ、以下の流れで受付を行いました。 予め導線を作っておくことで、受付で停滞することなく非常にスムーズに会場へ誘導することができました。 一方で、会場までの誘導が行き届いていなかったのは反省点。次回の改善点としてメモです📝 通訳 KTCは多国籍なメンバーで構成されており、英語のほうが得意なメンバーが多数所属しています。今回は2023年の総括かつ2024年のキックオフということで経営層の大事な話も入るため、本部会本編は全コンテンツ通訳を入れることになりました。しかし、2時間半にも及ぶ本編を逐次通訳するのは素人では到底無理です🤦‍♀️ そこで、以前からオリエンテーションの通訳などでお世話になっている通訳会社様にお願いすることにしました。 🔻ZOOMでの通訳は通訳機能をONにしておくと言語チャネルを切り替えられるようになっています🔻 通訳者様が耳で日本語を聞き👂、そのまま英語チャネルで英語で発話🗣️することで、英語チャネルには英語音声が流れる仕組みです。 設定の方法はこちら👉 ミーティングまたはウェビナーでの言語通訳の使用 運営チーム内の通訳担当は現地にいない通訳者様とコミュニケーションを取り、音声・映像トラブルや会場の様子などを適宜コミュニケーションします。通訳があることで、経営層のメッセージを的確に伝えることができました。プロの通訳者様には頭が上がりません🙇‍♀️ 2023年の漢字 2022年末も実施したこの企画。各グループからマネージャーが登壇し、1年を表す漢字と総括、そして新しい1年に向けた意気込みを共有します。 事前に22グループの回答を取りまとめて当日の資料に反映させる作業を担当者にお願いしました。 忙しいマネージャー陣にお願いすることになるので、12月中旬に案内、1月19日の〆切です。 ![kanji_announce](/assets/blog/authors/M.Mori/20240611/kanji_announce.png =500x) 🔻こちらは旧テックブログチーム(現技術広報グループ)のもの。 ![kanji_blog](/assets/blog/authors/M.Mori/20240611/kanji_blog.png =700x) 🔺こんな感じでConfluenceに各グループの内容をまとめていただき、 🔻こんな感じに資料に落とし込んでいきました。 ![kanji_blog_ppt](/assets/blog/authors/M.Mori/20240611/kanji_blog_ppt.png =700x) 各グループのカラーが出ていておもしろかったのと、各グループのやっていたこと・やっていくことが知れる滅多にない機会になりました! K-1グランプリ 何といっても今回の目玉企画です。弊社では毎月「景山賞」と称して特筆すべき案件や活動を表彰しています。 👉 参考記事: 全社員ミーティングをテコ入れした話 業務の振り返りと業務価値の再認識そして部署を超えた情報共有が目的ですが、これの年度賞版をK-1グランプリと称して行うことになりました。 大まかな流れは下図の通りです。 月次賞ではプレゼンは行いませんが、今回は年度賞。プレゼン力も問われます。 グループの数が多いため、まずは各グループから案件をエントリーしてもらい、その中から各部代表案件をひとつずつ選出してもらいました。 私はプラットフォーム部の選考会に賑やかしとして参加させていただいたのですが、普段違うグループで働いている メンバーを互いに称賛しあう場 になっていたのが印象的でした。 アナウンス時や予選会、当日まで通してお伝えし続けてきたのは、K-1GPは年度賞ですが、決して優劣をつけることが目的ではないということです。 この1年、皆さんが従事してきた仕事は全て素晴らしいものであることは大前提です。 K-1GPの一番の目的は自身の業務を振り返り、お互いの仕事を称賛し合うことだったので、少なくとも私の参加したプラットフォーム部の予選会では、この 「互いに称賛し合う姿」 が見られて非常にうれしかったです。 こうして予選会で選出された代表案件は、それぞれ本部会までの1週間で各3分のプレゼン資料を準備いただき当日を迎えました。 非常にタイトなスケジュールで準備をいただくことになり、代表者の皆さんには感謝感謝です🙇‍♀️ 集まっていくプレゼン資料はそれぞれ個性に溢れていて、毎日格納される資料をワクワクして待っていました。笑 社長・副社長ごあいさつ 2024年のキックオフということで、小寺社長と景山副社長からのごあいさつも大きなコンテンツでした。 毎月の全体ミーティング直接お話を聞く機会はなく、特に小寺さんに関してはKINTO/KTC合同の場でしかお話いただくことがなかったため、非常に重要な場でした。 明確なトップメッセージを全員が聞くことで同じ方向を向いて仕事をすることができます。いわば軸のようなものです。 運営メンバーで事前に「KTCのエンジニアにどのようになってほしいか」「2024年KTCにどのようなことを求めるか」をすり合わせたり、 逆にメンバー目線で「こういったことをぜひ発信いただきたい」ということをお伝えしたりして全体構成をまとめていきました。 スライドはより伝わりやすいよう、我らがデザイナー軍団クリエイティブ室にお力添えいただきました。 外国籍メンバーにも誤解の無いような言葉を選んだり、ビジュアルで補完したり。 ![president_message](/assets/blog/authors/M.Mori/20240611/president_message.jpg =500x) 社長メッセージをビジュアル化 今回トヨタの新しいビジョン 「次の道を発明しよう」 (Inventing our path forward together) がタイミング良く発表され、こちらも改めて社長よりシェアされました。 ![toyota_message](/assets/blog/authors/M.Mori/20240611/toyota_message.jpg =500x) Inventing our path forward together 懇親会 さて、オフラインイベントの醍醐味といえば懇親会です。 今回は会場指定のケータリングを利用させていただきましたが、ロゴ入りハンバーガーや飾りつけもすることができ、とても豪華になりました ✨ ![logo_burger](/assets/blog/authors/M.Mori/20240611/logo_burger.jpg =500x) ケータリングはホワイエに用意し、本会場には何も置かなかったので、ご飯や飲み物を取りに行きにくかったのは反省点です。 さて、今回の乾杯は「鏡開き」にて行いました。 運営メンバーみんな初めての生鏡開きだったので、事前に調べたところ「バールや大きなカッターが必要」と出てきて非常に焦りました。 が、なんとそんな必要のない非常にお手軽なオリジナル樽を KURAND様のサイト [^1]で発見し、こちらを採用。 [^1]: KURAND様はこのご縁もあり、後日弊社主催のイベント 「ソースコードレビュー」まつり にご協賛いただきました。 ![kagamibiraki](/assets/blog/authors/M.Mori/20240611/kagamibiraki.jpg =500x) めちゃくちゃかわいくないですか!? このオリジナルデザインはこちらも我らがクリエイティブ室のデザインです 💯 乾杯後は基本フリーではありましたが、なんといっても260人規模です。普段会話しない人とも会話してほしいのが運営の想い。 何か話のきっかけにできるものを検討しました。 当初はチーム分けしてゲームするか?と話していましたが、大人数すぎるし、強制参加もさせたくないし...と悩んでいたところで運営が見つけたのが Rally でした。 スマホで簡単にスタンプラリーができるサービスです。QRを読み込んでスタンプラリーができるので、このQRを各部ごとに配布すれば交流ができるのでは...!?即決でした。 フリープランでもいろいろとカスタマイズでき、1週間でけっこうな完成度のものができました。 🔻Rallyの使い方はこんな感じ。 ![rally_slides](/assets/blog/authors/M.Mori/20240611/rally_slides.jpg =700x) 受付で配布したQRコードシールが各自のIDケースに貼られているので、それを読み取ってスタンプを集める形式です。 準備の手軽さとコミュニケーションの促進という意味では非常に良かったです。非常に良かった。 強制参加させることもなく、スムーズに違う部署の人に声をかけあってる姿がもはや感動的でした。 ![rally_poster](/assets/blog/authors/M.Mori/20240611/rally_poster.jpg =500x) 当日掲示したポスター ノベルティ さて、事前準備編ということでもう一つ忘れてはいけない準備物がノベルティです。 タイトなスケジュールだったため、必要なものを最初に洗いだせておらず、クリエイティブ室の皆様にはかなり無理を言ってたくさんのものを作っていただきました。。 K-1 GPロゴ 表彰状 ![idcase](/assets/blog/authors/M.Mori/20240611/design_k1_logo.png =300x) ![award](/assets/blog/authors/M.Mori/20240611/design_award.jpg =300x) スライドマスタ 鏡割り用の樽デザイン ![slidemaster](/assets/blog/authors/M.Mori/20240611/slide_master.jpg =300x) ![sakadaru](/assets/blog/authors/M.Mori/20240611/design_sakadaru.png =300x) IDカードケース(全員配布) スタッフTシャツ ![idcase](/assets/blog/authors/M.Mori/20240611/design_idcase.jpg =300x) ![staff_shirts](/assets/blog/authors/M.Mori/20240611/design_staff_t.jpg =300x) タンブラー(スタンプラリー景品) エコバッグ (スタンプラリー景品) ![tumbler](/assets/blog/authors/M.Mori/20240611/design_tumbler.jpg =300x) ![eco_bag](/assets/blog/authors/M.Mori/20240611/design_bag.jpg =300x) 改めて見ても「どんなけ作らせるねん!?」とツッコミたくなるレベルですね。笑 これに加えて社内エンジニアには各自の名札を自動で作成できるツールを作成してもらいました。 🔻Slackアイコン・部署・名前・KTCロゴが全員分印字されます。 ![Name_card](/assets/blog/authors/M.Mori/20240611/namecard.jpg =300x) 「こんなのあったらいいな」と軽く言ってみたらほんとにすぐに作ってくれました。 自社ながら、KTCメンバーの仕事の速さとクオリティの高さには毎度驚かされます。 本業がある中でもご協力いただいた方々にこの場をお借りして改めて深く感謝します 🙇‍♀️🙇‍♀️🙇‍♀️ 運営してみた学び・次回開催に向けて もう半年も経ちましたが、こうしてやったことを書き出してみると、よく準備したなぁ…笑 今回の記事執筆でこのキックオフ会をふり返ってみて、改めて「組織のビジョンや目標をわかりやすく全社に共有すること」「オフラインでチームビルディングを行うこと」の重要性を認識しました。 経営層から直接ビジョンや戦略が伝えられるだけで、その考えやダイレクションに基づいて同じ方向を向いて日々職務に従事することができます。また、この考えに共感できれば、社員のモチベーションアップにもつながります。これをオフラインで行うことにより、そのダイレクションは浸透しやすくなり、社員と経営層、さらには社員同士にも信頼関係が生まれ、疑問や不安の解消にも役立ちます。 特に弊社はKINTOサービススタートから5年経ち、会社としても次のステージに向かう最中。このタイミングでこういったイベントを行うことが、組織全体のエンゲージメント向上や、一体感の醸成に繋がるのだと実感しました✨ また別の記事などで実施結果もお伝えできると思いますが、参加者の声としても「仕事へのモチベーションが上がった」「他のチームが何をしているか、認識が強まった」「経営層の考えを知ることができた」など非常に好意的な反応が多く、実施した甲斐があったな、と思いました😄 こういったイベントはぜひ1年に一度は開催したく、次回開催に向けて運営の学びを活かし、至らない点は反省点としてさらなる改善を目指します💪 気づけば7000文字以上も書いてしまいましたが、それだけ思い入れのあったイベントだったということで。。 最後まで読んでいただきありがとうございます!KINTOテクノロジーズでは今後も社内外様々なイベントを計画中です! 社外向けイベントは 弊社Connpass にて随時募集しますので、ご興味あればぜひご参加ください 😄
Hello Hi there, my name is Murayama, and I work as an assistant at the CIO office at KINTO Technologies. This article will introduce our employees' office and desk setups in a relaxed manner (˘ω˘) Introduction to Our Offices This is our head office in Nagoya. President Kotera-san’s strong vision is reflected in the interior, emphasizing natural elements and brightness. The fire pit you can see in the bottom right picture -which is Kotera-san's particular point of focus-, is lit during certain times. It's located in the center of the office, where everyone gathers to have lunch together! The second location is the Muromachi office. Our Muromachi office located in Tokyo has two floors. It also has this area we call “the Junction”. It's a very elegant spot, also used for video and photoshoots! It's conveniently located near many shops since it's housed inside of the COREDO Muromachi 2 building. In this area, you can find whatever you want to eat! The third location is the Jimbocho office. I saw the Platform Group gathered in the big conference room so I took a picture of them. The Jimbocho office is popular because it has the largest number of conference rooms. This area offers affordable lunch options, especially there are a lot of delicious curry restaurants! I always have curry whenever I visit 🍛 The photo in the bottom right corner is a vending machine with the KINTO Technologies logo at this office. The fourth site is the Osaka Tech Lab. Not ‘office’, but ‘Tech Lab’! (This is important) It opened in April and has still few employees, but everyone there shares their opinions to improve it. The rooftop in the bottom right part of this photo is wide and popular. Lunch is also cheap around Shinsaibashi! Plus, Osaka's batter-based dishes are delicious! Although I'm from Kanto, so I’m not used to okonomiyaki set meals for lunch... Introduction to Our Desk Setups Each person personalizes their seat to work comfortably. Functional desks reflect having a good setup, I’m sure, but I don’t think its only about functionality. This is mine. I have a big cheering squad. It's a great desk setup, right?! Our vice president Kageyama-san also has some on his desk. Every once in a while, one of them rolls off somewhere and I find it heartwarming and funny to see Kageyama-san search for it. It's inevitable when you hold a Sylvanian Families figurine in your hands, it brings out your nurturing instincts. Before I make this blog all about Sylvanian Families, let's move on to the next desk around here. ![Employee commentary](/assets/blog/authors/uka/member-02.jpg =450x) Cool keyboard! She enjoys building her own PCs and Gunpla. Great hobby! In her home desk setup, she has many Gunplas watching her. She seems to have also brought a small one into the office today. I gave her a Sylvania so she has even more friends now. By now, I'm one of the Sylvanian Families evangelists in office! ![Employee commentary](/assets/blog/authors/uka/member-03.jpg =450x) I'm sharing all this informally, but please know that I also perform well at my job. There’s an e-sports club in the KINTO Technologies community, and we all played Splatoon together the other day. As I work at an IT company, I naturally (I guess?) love games as well. The recent trend in the company is playing Mahjong! ![Employee commentary](/assets/blog/authors/uka/member-04.jpg =450x) Se says she wants Doraemon's Anywhere Door and I can relate. I wish I could easily travel back and forth between the different offices... But setting wishes aside, whenever I am needed, I travel to the other offices too. Each office has its own good points and I enjoy working in all of them! ![Employee commentary](/assets/blog/authors/uka/member-05.jpg =450x) There are many people here who like books and the company has a system to lend them but It's also common to see employees lending books to each other. Also, I learned about Slack after joining KINTO Technologies. It's a wonderful application filled with cute emoji's and it allows us to communicate with each other in a nice and informal way! ![Employee commentary](/assets/blog/authors/uka/member-06.jpg =450x) This setup is super engineer-like, with its double display!! It's a wonderful desk with both functional aspects and modest comfort. By now, you should understand that Sylvanian Families are universally appealing, right? Finally Remote work is popular these days, but I think it is best to go to office and work with everyone face to face in an atmosphere that you enjoy (˘ω˘) On top of that, you are free to change your hair color, clothes, and desk setup, allowing you to work in a comfortable environment, which makes it more enjoyable! Thank you for reading till the end!