notelm-mcp 1.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (323) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +456 -0
  3. package/dist/auth/auth-manager.d.ts +139 -0
  4. package/dist/auth/auth-manager.d.ts.map +1 -0
  5. package/dist/auth/auth-manager.js +960 -0
  6. package/dist/auth/auth-manager.js.map +1 -0
  7. package/dist/config.d.ts +92 -0
  8. package/dist/config.d.ts.map +1 -0
  9. package/dist/config.js +219 -0
  10. package/dist/config.js.map +1 -0
  11. package/dist/constants.d.ts +58 -0
  12. package/dist/constants.d.ts.map +1 -0
  13. package/dist/constants.js +133 -0
  14. package/dist/constants.js.map +1 -0
  15. package/dist/errors.d.ts +26 -0
  16. package/dist/errors.d.ts.map +1 -0
  17. package/dist/errors.js +41 -0
  18. package/dist/errors.js.map +1 -0
  19. package/dist/index.d.ts +32 -0
  20. package/dist/index.d.ts.map +1 -0
  21. package/dist/index.js +325 -0
  22. package/dist/index.js.map +1 -0
  23. package/dist/library/notebook-library.d.ts +70 -0
  24. package/dist/library/notebook-library.d.ts.map +1 -0
  25. package/dist/library/notebook-library.js +279 -0
  26. package/dist/library/notebook-library.js.map +1 -0
  27. package/dist/library/types.d.ts +67 -0
  28. package/dist/library/types.d.ts.map +1 -0
  29. package/dist/library/types.js +8 -0
  30. package/dist/library/types.js.map +1 -0
  31. package/dist/playwright.config.d.ts +3 -0
  32. package/dist/playwright.config.d.ts.map +1 -0
  33. package/dist/playwright.config.js +38 -0
  34. package/dist/playwright.config.js.map +1 -0
  35. package/dist/resources/resource-handlers.d.ts +22 -0
  36. package/dist/resources/resource-handlers.d.ts.map +1 -0
  37. package/dist/resources/resource-handlers.js +216 -0
  38. package/dist/resources/resource-handlers.js.map +1 -0
  39. package/dist/scripts/save-auth-state.d.ts +2 -0
  40. package/dist/scripts/save-auth-state.d.ts.map +1 -0
  41. package/dist/scripts/save-auth-state.js +91 -0
  42. package/dist/scripts/save-auth-state.js.map +1 -0
  43. package/dist/session/browser-session.d.ts +108 -0
  44. package/dist/session/browser-session.d.ts.map +1 -0
  45. package/dist/session/browser-session.js +636 -0
  46. package/dist/session/browser-session.js.map +1 -0
  47. package/dist/session/session-manager.d.ts +76 -0
  48. package/dist/session/session-manager.d.ts.map +1 -0
  49. package/dist/session/session-manager.js +273 -0
  50. package/dist/session/session-manager.js.map +1 -0
  51. package/dist/session/shared-context-manager.d.ts +107 -0
  52. package/dist/session/shared-context-manager.d.ts.map +1 -0
  53. package/dist/session/shared-context-manager.js +447 -0
  54. package/dist/session/shared-context-manager.js.map +1 -0
  55. package/dist/src/auth/auth-manager.d.ts +139 -0
  56. package/dist/src/auth/auth-manager.d.ts.map +1 -0
  57. package/dist/src/auth/auth-manager.js +960 -0
  58. package/dist/src/auth/auth-manager.js.map +1 -0
  59. package/dist/src/config.d.ts +92 -0
  60. package/dist/src/config.d.ts.map +1 -0
  61. package/dist/src/config.js +219 -0
  62. package/dist/src/config.js.map +1 -0
  63. package/dist/src/constants.d.ts +58 -0
  64. package/dist/src/constants.d.ts.map +1 -0
  65. package/dist/src/constants.js +133 -0
  66. package/dist/src/constants.js.map +1 -0
  67. package/dist/src/errors.d.ts +26 -0
  68. package/dist/src/errors.d.ts.map +1 -0
  69. package/dist/src/errors.js +41 -0
  70. package/dist/src/errors.js.map +1 -0
  71. package/dist/src/index.d.ts +32 -0
  72. package/dist/src/index.d.ts.map +1 -0
  73. package/dist/src/index.js +325 -0
  74. package/dist/src/index.js.map +1 -0
  75. package/dist/src/library/notebook-library.d.ts +70 -0
  76. package/dist/src/library/notebook-library.d.ts.map +1 -0
  77. package/dist/src/library/notebook-library.js +279 -0
  78. package/dist/src/library/notebook-library.js.map +1 -0
  79. package/dist/src/library/types.d.ts +67 -0
  80. package/dist/src/library/types.d.ts.map +1 -0
  81. package/dist/src/library/types.js +8 -0
  82. package/dist/src/library/types.js.map +1 -0
  83. package/dist/src/resources/resource-handlers.d.ts +22 -0
  84. package/dist/src/resources/resource-handlers.d.ts.map +1 -0
  85. package/dist/src/resources/resource-handlers.js +216 -0
  86. package/dist/src/resources/resource-handlers.js.map +1 -0
  87. package/dist/src/scripts/health-check.d.ts +13 -0
  88. package/dist/src/scripts/health-check.d.ts.map +1 -0
  89. package/dist/src/scripts/health-check.js +100 -0
  90. package/dist/src/scripts/health-check.js.map +1 -0
  91. package/dist/src/session/browser-session.d.ts +108 -0
  92. package/dist/src/session/browser-session.d.ts.map +1 -0
  93. package/dist/src/session/browser-session.js +642 -0
  94. package/dist/src/session/browser-session.js.map +1 -0
  95. package/dist/src/session/session-manager.d.ts +76 -0
  96. package/dist/src/session/session-manager.d.ts.map +1 -0
  97. package/dist/src/session/session-manager.js +273 -0
  98. package/dist/src/session/session-manager.js.map +1 -0
  99. package/dist/src/session/shared-context-manager.d.ts +107 -0
  100. package/dist/src/session/shared-context-manager.d.ts.map +1 -0
  101. package/dist/src/session/shared-context-manager.js +447 -0
  102. package/dist/src/session/shared-context-manager.js.map +1 -0
  103. package/dist/src/tools/definitions/ask-question.d.ts +8 -0
  104. package/dist/src/tools/definitions/ask-question.d.ts.map +1 -0
  105. package/dist/src/tools/definitions/ask-question.js +211 -0
  106. package/dist/src/tools/definitions/ask-question.js.map +1 -0
  107. package/dist/src/tools/definitions/notebook-management.d.ts +3 -0
  108. package/dist/src/tools/definitions/notebook-management.d.ts.map +1 -0
  109. package/dist/src/tools/definitions/notebook-management.js +243 -0
  110. package/dist/src/tools/definitions/notebook-management.js.map +1 -0
  111. package/dist/src/tools/definitions/session-management.d.ts +3 -0
  112. package/dist/src/tools/definitions/session-management.d.ts.map +1 -0
  113. package/dist/src/tools/definitions/session-management.js +41 -0
  114. package/dist/src/tools/definitions/session-management.js.map +1 -0
  115. package/dist/src/tools/definitions/system.d.ts +3 -0
  116. package/dist/src/tools/definitions/system.d.ts.map +1 -0
  117. package/dist/src/tools/definitions/system.js +143 -0
  118. package/dist/src/tools/definitions/system.js.map +1 -0
  119. package/dist/src/tools/definitions.d.ts +12 -0
  120. package/dist/src/tools/definitions.d.ts.map +1 -0
  121. package/dist/src/tools/definitions.js +26 -0
  122. package/dist/src/tools/definitions.js.map +1 -0
  123. package/dist/src/tools/handlers.d.ts +212 -0
  124. package/dist/src/tools/handlers.d.ts.map +1 -0
  125. package/dist/src/tools/handlers.js +712 -0
  126. package/dist/src/tools/handlers.js.map +1 -0
  127. package/dist/src/tools/index.d.ts +8 -0
  128. package/dist/src/tools/index.d.ts.map +1 -0
  129. package/dist/src/tools/index.js +8 -0
  130. package/dist/src/tools/index.js.map +1 -0
  131. package/dist/src/types.d.ts +88 -0
  132. package/dist/src/types.d.ts.map +1 -0
  133. package/dist/src/types.js +5 -0
  134. package/dist/src/types.js.map +1 -0
  135. package/dist/src/utils/auth-manager.d.ts +2 -0
  136. package/dist/src/utils/auth-manager.d.ts.map +1 -0
  137. package/dist/src/utils/auth-manager.js +25 -0
  138. package/dist/src/utils/auth-manager.js.map +1 -0
  139. package/dist/src/utils/cleanup-manager.d.ts +133 -0
  140. package/dist/src/utils/cleanup-manager.d.ts.map +1 -0
  141. package/dist/src/utils/cleanup-manager.js +673 -0
  142. package/dist/src/utils/cleanup-manager.js.map +1 -0
  143. package/dist/src/utils/cli-handler.d.ts +16 -0
  144. package/dist/src/utils/cli-handler.d.ts.map +1 -0
  145. package/dist/src/utils/cli-handler.js +102 -0
  146. package/dist/src/utils/cli-handler.js.map +1 -0
  147. package/dist/src/utils/logger.d.ts +61 -0
  148. package/dist/src/utils/logger.d.ts.map +1 -0
  149. package/dist/src/utils/logger.js +92 -0
  150. package/dist/src/utils/logger.js.map +1 -0
  151. package/dist/src/utils/page-utils.d.ts +54 -0
  152. package/dist/src/utils/page-utils.d.ts.map +1 -0
  153. package/dist/src/utils/page-utils.js +381 -0
  154. package/dist/src/utils/page-utils.js.map +1 -0
  155. package/dist/src/utils/rate-limit-handler.d.ts +42 -0
  156. package/dist/src/utils/rate-limit-handler.d.ts.map +1 -0
  157. package/dist/src/utils/rate-limit-handler.js +88 -0
  158. package/dist/src/utils/rate-limit-handler.js.map +1 -0
  159. package/dist/src/utils/rate-limit-handler.test.d.ts +7 -0
  160. package/dist/src/utils/rate-limit-handler.test.d.ts.map +1 -0
  161. package/dist/src/utils/rate-limit-handler.test.js +86 -0
  162. package/dist/src/utils/rate-limit-handler.test.js.map +1 -0
  163. package/dist/src/utils/settings-manager.d.ts +37 -0
  164. package/dist/src/utils/settings-manager.d.ts.map +1 -0
  165. package/dist/src/utils/settings-manager.js +121 -0
  166. package/dist/src/utils/settings-manager.js.map +1 -0
  167. package/dist/src/utils/stealth-utils.d.ts +135 -0
  168. package/dist/src/utils/stealth-utils.d.ts.map +1 -0
  169. package/dist/src/utils/stealth-utils.js +396 -0
  170. package/dist/src/utils/stealth-utils.js.map +1 -0
  171. package/dist/src/utils/stealth-utils.test.d.ts +7 -0
  172. package/dist/src/utils/stealth-utils.test.d.ts.map +1 -0
  173. package/dist/src/utils/stealth-utils.test.js +72 -0
  174. package/dist/src/utils/stealth-utils.test.js.map +1 -0
  175. package/dist/tests/e2e/authenticated.spec.d.ts +2 -0
  176. package/dist/tests/e2e/authenticated.spec.d.ts.map +1 -0
  177. package/dist/tests/e2e/authenticated.spec.js +41 -0
  178. package/dist/tests/e2e/authenticated.spec.js.map +1 -0
  179. package/dist/tests/e2e/mocked.spec.d.ts +2 -0
  180. package/dist/tests/e2e/mocked.spec.d.ts.map +1 -0
  181. package/dist/tests/e2e/mocked.spec.js +32 -0
  182. package/dist/tests/e2e/mocked.spec.js.map +1 -0
  183. package/dist/tests/mocks/handlers.d.ts +4 -0
  184. package/dist/tests/mocks/handlers.d.ts.map +1 -0
  185. package/dist/tests/mocks/handlers.js +55 -0
  186. package/dist/tests/mocks/handlers.js.map +1 -0
  187. package/dist/tests/mocks/setup.d.ts +3 -0
  188. package/dist/tests/mocks/setup.d.ts.map +1 -0
  189. package/dist/tests/mocks/setup.js +77 -0
  190. package/dist/tests/mocks/setup.js.map +1 -0
  191. package/dist/tools/definitions/ask-question.d.ts +8 -0
  192. package/dist/tools/definitions/ask-question.d.ts.map +1 -0
  193. package/dist/tools/definitions/ask-question.js +211 -0
  194. package/dist/tools/definitions/ask-question.js.map +1 -0
  195. package/dist/tools/definitions/notebook-management.d.ts +3 -0
  196. package/dist/tools/definitions/notebook-management.d.ts.map +1 -0
  197. package/dist/tools/definitions/notebook-management.js +243 -0
  198. package/dist/tools/definitions/notebook-management.js.map +1 -0
  199. package/dist/tools/definitions/session-management.d.ts +3 -0
  200. package/dist/tools/definitions/session-management.d.ts.map +1 -0
  201. package/dist/tools/definitions/session-management.js +41 -0
  202. package/dist/tools/definitions/session-management.js.map +1 -0
  203. package/dist/tools/definitions/system.d.ts +3 -0
  204. package/dist/tools/definitions/system.d.ts.map +1 -0
  205. package/dist/tools/definitions/system.js +143 -0
  206. package/dist/tools/definitions/system.js.map +1 -0
  207. package/dist/tools/definitions.d.ts +12 -0
  208. package/dist/tools/definitions.d.ts.map +1 -0
  209. package/dist/tools/definitions.js +26 -0
  210. package/dist/tools/definitions.js.map +1 -0
  211. package/dist/tools/handlers.d.ts +212 -0
  212. package/dist/tools/handlers.d.ts.map +1 -0
  213. package/dist/tools/handlers.js +712 -0
  214. package/dist/tools/handlers.js.map +1 -0
  215. package/dist/tools/index.d.ts +8 -0
  216. package/dist/tools/index.d.ts.map +1 -0
  217. package/dist/tools/index.js +8 -0
  218. package/dist/tools/index.js.map +1 -0
  219. package/dist/types.d.ts +82 -0
  220. package/dist/types.d.ts.map +1 -0
  221. package/dist/types.js +5 -0
  222. package/dist/types.js.map +1 -0
  223. package/dist/utils/cleanup-manager.d.ts +133 -0
  224. package/dist/utils/cleanup-manager.d.ts.map +1 -0
  225. package/dist/utils/cleanup-manager.js +673 -0
  226. package/dist/utils/cleanup-manager.js.map +1 -0
  227. package/dist/utils/cli-handler.d.ts +16 -0
  228. package/dist/utils/cli-handler.d.ts.map +1 -0
  229. package/dist/utils/cli-handler.js +102 -0
  230. package/dist/utils/cli-handler.js.map +1 -0
  231. package/dist/utils/logger.d.ts +61 -0
  232. package/dist/utils/logger.d.ts.map +1 -0
  233. package/dist/utils/logger.js +92 -0
  234. package/dist/utils/logger.js.map +1 -0
  235. package/dist/utils/page-utils.d.ts +54 -0
  236. package/dist/utils/page-utils.d.ts.map +1 -0
  237. package/dist/utils/page-utils.js +381 -0
  238. package/dist/utils/page-utils.js.map +1 -0
  239. package/dist/utils/rate-limit-handler.d.ts +42 -0
  240. package/dist/utils/rate-limit-handler.d.ts.map +1 -0
  241. package/dist/utils/rate-limit-handler.js +88 -0
  242. package/dist/utils/rate-limit-handler.js.map +1 -0
  243. package/dist/utils/rate-limit-handler.test.d.ts +7 -0
  244. package/dist/utils/rate-limit-handler.test.d.ts.map +1 -0
  245. package/dist/utils/rate-limit-handler.test.js +91 -0
  246. package/dist/utils/rate-limit-handler.test.js.map +1 -0
  247. package/dist/utils/settings-manager.d.ts +37 -0
  248. package/dist/utils/settings-manager.d.ts.map +1 -0
  249. package/dist/utils/settings-manager.js +121 -0
  250. package/dist/utils/settings-manager.js.map +1 -0
  251. package/dist/utils/stealth-utils.d.ts +135 -0
  252. package/dist/utils/stealth-utils.d.ts.map +1 -0
  253. package/dist/utils/stealth-utils.js +396 -0
  254. package/dist/utils/stealth-utils.js.map +1 -0
  255. package/dist/utils/stealth-utils.test.d.ts +7 -0
  256. package/dist/utils/stealth-utils.test.d.ts.map +1 -0
  257. package/dist/utils/stealth-utils.test.js +72 -0
  258. package/dist/utils/stealth-utils.test.js.map +1 -0
  259. package/docs/01_configuration.md +94 -0
  260. package/docs/02_tools.md +34 -0
  261. package/docs/03_troubleshooting.md +59 -0
  262. package/docs/04_usage-guide.md +245 -0
  263. package/docs/05_project-analysis.qmd +309 -0
  264. package/docs/06_integration-analysis.html +914 -0
  265. package/docs/06_integration-analysis.qmd +255 -0
  266. package/docs/06_integration-analysis_files/libs/bootstrap/bootstrap-4f0954b6b0dd6bf39f4bb9151ba984db.min.css +12 -0
  267. package/docs/06_integration-analysis_files/libs/bootstrap/bootstrap-icons.css +2106 -0
  268. package/docs/06_integration-analysis_files/libs/bootstrap/bootstrap-icons.woff +0 -0
  269. package/docs/06_integration-analysis_files/libs/bootstrap/bootstrap.min.js +7 -0
  270. package/docs/06_integration-analysis_files/libs/clipboard/clipboard.min.js +7 -0
  271. package/docs/06_integration-analysis_files/libs/quarto-html/anchor.min.js +9 -0
  272. package/docs/06_integration-analysis_files/libs/quarto-html/axe/axe-check.js +145 -0
  273. package/docs/06_integration-analysis_files/libs/quarto-html/popper.min.js +6 -0
  274. package/docs/06_integration-analysis_files/libs/quarto-html/quarto-syntax-highlighting-587c61ba64f3a5504c4d52d930310e48.css +236 -0
  275. package/docs/06_integration-analysis_files/libs/quarto-html/quarto.js +847 -0
  276. package/docs/06_integration-analysis_files/libs/quarto-html/tabsets/tabsets.js +95 -0
  277. package/docs/06_integration-analysis_files/libs/quarto-html/tippy.css +1 -0
  278. package/docs/06_integration-analysis_files/libs/quarto-html/tippy.umd.min.js +2 -0
  279. package/docs/07_e2e-testing-safety.qmd +754 -0
  280. package/docs/08_project-re-evaluation.html +609 -0
  281. package/docs/08_project-re-evaluation.qmd +86 -0
  282. package/docs/08_project-re-evaluation_files/libs/bootstrap/bootstrap-4f0954b6b0dd6bf39f4bb9151ba984db.min.css +12 -0
  283. package/docs/08_project-re-evaluation_files/libs/bootstrap/bootstrap-icons.css +2106 -0
  284. package/docs/08_project-re-evaluation_files/libs/bootstrap/bootstrap-icons.woff +0 -0
  285. package/docs/08_project-re-evaluation_files/libs/bootstrap/bootstrap.min.js +7 -0
  286. package/docs/08_project-re-evaluation_files/libs/clipboard/clipboard.min.js +7 -0
  287. package/docs/08_project-re-evaluation_files/libs/quarto-html/anchor.min.js +9 -0
  288. package/docs/08_project-re-evaluation_files/libs/quarto-html/axe/axe-check.js +145 -0
  289. package/docs/08_project-re-evaluation_files/libs/quarto-html/popper.min.js +6 -0
  290. package/docs/08_project-re-evaluation_files/libs/quarto-html/quarto-syntax-highlighting-587c61ba64f3a5504c4d52d930310e48.css +236 -0
  291. package/docs/08_project-re-evaluation_files/libs/quarto-html/quarto.js +847 -0
  292. package/docs/08_project-re-evaluation_files/libs/quarto-html/tabsets/tabsets.js +95 -0
  293. package/docs/08_project-re-evaluation_files/libs/quarto-html/tippy.css +1 -0
  294. package/docs/08_project-re-evaluation_files/libs/quarto-html/tippy.umd.min.js +2 -0
  295. package/docs/notebooklm-mcp-usage.html +704 -0
  296. package/docs/notebooklm-mcp-usage.qmd +119 -0
  297. package/docs/notebooklm-mcp-usage_files/libs/bootstrap/bootstrap-6b71f2156b6a5230c6677325978bcf08.min.css +12 -0
  298. package/docs/notebooklm-mcp-usage_files/libs/bootstrap/bootstrap-icons.css +2106 -0
  299. package/docs/notebooklm-mcp-usage_files/libs/bootstrap/bootstrap-icons.woff +0 -0
  300. package/docs/notebooklm-mcp-usage_files/libs/bootstrap/bootstrap.min.js +7 -0
  301. package/docs/notebooklm-mcp-usage_files/libs/clipboard/clipboard.min.js +7 -0
  302. package/docs/notebooklm-mcp-usage_files/libs/quarto-html/anchor.min.js +9 -0
  303. package/docs/notebooklm-mcp-usage_files/libs/quarto-html/axe/axe-check.js +145 -0
  304. package/docs/notebooklm-mcp-usage_files/libs/quarto-html/popper.min.js +6 -0
  305. package/docs/notebooklm-mcp-usage_files/libs/quarto-html/quarto-syntax-highlighting-587c61ba64f3a5504c4d52d930310e48.css +236 -0
  306. package/docs/notebooklm-mcp-usage_files/libs/quarto-html/quarto.js +847 -0
  307. package/docs/notebooklm-mcp-usage_files/libs/quarto-html/tabsets/tabsets.js +95 -0
  308. package/docs/notebooklm-mcp-usage_files/libs/quarto-html/tippy.css +1 -0
  309. package/docs/notebooklm-mcp-usage_files/libs/quarto-html/tippy.umd.min.js +2 -0
  310. package/docs/repomix-usage_files/libs/bootstrap/bootstrap-6b71f2156b6a5230c6677325978bcf08.min.css +12 -0
  311. package/docs/repomix-usage_files/libs/bootstrap/bootstrap-icons.css +2106 -0
  312. package/docs/repomix-usage_files/libs/bootstrap/bootstrap-icons.woff +0 -0
  313. package/docs/repomix-usage_files/libs/bootstrap/bootstrap.min.js +7 -0
  314. package/docs/repomix-usage_files/libs/clipboard/clipboard.min.js +7 -0
  315. package/docs/repomix-usage_files/libs/quarto-html/anchor.min.js +9 -0
  316. package/docs/repomix-usage_files/libs/quarto-html/axe/axe-check.js +145 -0
  317. package/docs/repomix-usage_files/libs/quarto-html/popper.min.js +6 -0
  318. package/docs/repomix-usage_files/libs/quarto-html/quarto-syntax-highlighting-587c61ba64f3a5504c4d52d930310e48.css +236 -0
  319. package/docs/repomix-usage_files/libs/quarto-html/quarto.js +847 -0
  320. package/docs/repomix-usage_files/libs/quarto-html/tabsets/tabsets.js +95 -0
  321. package/docs/repomix-usage_files/libs/quarto-html/tippy.css +1 -0
  322. package/docs/repomix-usage_files/libs/quarto-html/tippy.umd.min.js +2 -0
  323. package/package.json +62 -0
@@ -0,0 +1,754 @@
1
+ ---
2
+ title: "E2E 테스트: Google 자동화 감지 회피 전략"
3
+ subtitle: "안전한 브라우저 자동화 테스트를 위한 종합 연구 가이드"
4
+ author: "NotebookLM MCP Team"
5
+ date: 2026-01-02
6
+ format:
7
+ html:
8
+ toc: true
9
+ toc-depth: 3
10
+ number-sections: true
11
+ highlight-style: github
12
+ code-fold: false
13
+ ---
14
+
15
+ ## 요약
16
+
17
+ Google의 자동화 감지 시스템은 매우 정교하며, 무분별한 자동화 테스트는 계정 정지나 차단으로 이어질 수 있습니다. 본 문서는 안전한 E2E 테스트를 위한 전략을 4단계로 분류하여 제시합니다.
18
+
19
+ > [!CAUTION]
20
+ > **계정 보호가 최우선입니다.** 실제 Google 계정으로 자동화 로그인을 시도하면 계정이 정지될 수 있습니다.
21
+
22
+ ### 권장 전략 우선순위
23
+
24
+ | 순위 | 전략 | 계정 위험도 | 구현 복잡도 | 테스트 신뢰도 |
25
+ |:---:|:---|:---:|:---:|:---:|
26
+ | 1 | 완전 모킹 (LoFi Mock) | ⭐ 0% | 중간 | 높음 |
27
+ | 2 | 사전인증 세션 재사용 | ⭐ 5% | 낮음 | 매우 높음 |
28
+ | 3 | Patchright 스텔스 | ⚠️ 20% | 낮음 | 높음 |
29
+ | 4 | 실제 인증 자동화 | 🔴 높음 | 낮음 | 매우 높음 |
30
+
31
+ ---
32
+
33
+ ## Google 자동화 감지 메커니즘
34
+
35
+ Google은 브라우저 자동화를 감지하기 위해 다중 계층 방어 시스템을 사용합니다.
36
+
37
+ ### 1단계: 브라우저 핑거프린트 분석
38
+
39
+ ```javascript
40
+ // Google이 감지하는 주요 자동화 신호
41
+ const detectionSignals = {
42
+ // 1. WebDriver 플래그
43
+ navigatorWebdriver: navigator.webdriver, // true면 자동화
44
+
45
+ // 2. User-Agent 문자열
46
+ headlessChrome: navigator.userAgent.includes('HeadlessChrome'),
47
+
48
+ // 3. Chrome DevTools Protocol (CDP) 사용
49
+ runtimeEnabled: typeof Runtime !== 'undefined', // CDP 활성화
50
+
51
+ // 4. 자동화 관련 플래그
52
+ automationControlled: window.navigator.plugins.length === 0,
53
+
54
+ // 5. Chrome 객체 부재
55
+ chromeAppMissing: !window.chrome?.app,
56
+ chromeCsiMissing: !window.chrome?.csi,
57
+ };
58
+ ```
59
+
60
+ ### 2단계: 행동 패턴 분석
61
+
62
+ | 감지 항목 | 정상 사용자 | 자동화 봇 |
63
+ |:---|:---|:---|
64
+ | 마우스 이동 | 곡선, 불규칙 | 직선, 순간이동 |
65
+ | 타이핑 속도 | 50-150 WPM, 오타 포함 | 매우 빠름, 일정 |
66
+ | 페이지 탐색 | 스크롤, 읽기 시간 | 즉시 대상 요소 접근 |
67
+ | 클릭 간격 | 불규칙 (0.5-3초) | 일정한 고정 값 |
68
+ | IP 패턴 | 일관된 지역 | 빈번한 변경 |
69
+
70
+ ### 3단계: reCAPTCHA v3 스코어링
71
+
72
+ ```mermaid
73
+ flowchart LR
74
+ A[사용자 접속] --> B{reCAPTCHA v3<br/>행동 분석}
75
+ B -->|Score 0.9| C[정상 사용자]
76
+ B -->|Score 0.5| D[경고 모니터링]
77
+ B -->|Score 0.1| E[봇 의심]
78
+ E --> F[챌린지 또는 차단]
79
+ ```
80
+
81
+ ### Google 계정 정지 조건
82
+
83
+ > [!WARNING]
84
+ > 다음 행동이 감지되면 계정 정지가 발생할 수 있습니다:
85
+
86
+ 1. **비정상적 로그인 패턴**: 동일 계정으로 짧은 시간 내 다수 로그인 시도
87
+ 2. **자동화된 로그인**: `navigator.webdriver` 감지
88
+ 3. **비정상적 API 요청**: 과도한 요청 빈도
89
+ 4. **의심스러운 위치**: 갑작스러운 지역 변경
90
+ 5. **서비스 약관 위반**: 자동화 접근 금지 서비스 이용
91
+
92
+ ---
93
+
94
+ ## 전략 1: 완전 모킹 (권장)
95
+
96
+ > [!TIP]
97
+ > **가장 안전한 접근법**: Google 서버에 전혀 접속하지 않고 테스트
98
+
99
+ ### 개념
100
+
101
+ ```mermaid
102
+ sequenceDiagram
103
+ participant Test as E2E Test
104
+ participant App as NotebookLM App
105
+ participant MSW as MSW Mock
106
+ participant Real as Google 서버
107
+
108
+ Test->>App: 페이지 로드
109
+ App->>MSW: OAuth 요청
110
+ MSW-->>App: 모킹된 응답
111
+ Note right of Real: 실제 서버 접속 없음
112
+ App-->>Test: 테스트 완료
113
+ ```
114
+
115
+ ### MSW (Mock Service Worker) 구현
116
+
117
+ ```typescript
118
+ // tests/mocks/handlers.ts
119
+ import { http, HttpResponse } from 'msw';
120
+
121
+ export const googleOAuthHandlers = [
122
+ // OAuth 인증 엔드포인트 모킹
123
+ http.get('https://accounts.google.com/o/oauth2/v2/auth', () => {
124
+ const mockAuthCode = 'mock_auth_code_12345';
125
+ return HttpResponse.redirect(
126
+ `http://localhost:3000/callback?code=${mockAuthCode}`
127
+ );
128
+ }),
129
+
130
+ // 토큰 교환 엔드포인트 모킹
131
+ http.post('https://oauth2.googleapis.com/token', () => {
132
+ return HttpResponse.json({
133
+ access_token: 'mock_access_token',
134
+ refresh_token: 'mock_refresh_token',
135
+ expires_in: 3600,
136
+ token_type: 'Bearer',
137
+ });
138
+ }),
139
+
140
+ // 사용자 정보 엔드포인트 모킹
141
+ http.get('https://www.googleapis.com/oauth2/v3/userinfo', () => {
142
+ return HttpResponse.json({
143
+ sub: '123456789',
144
+ name: 'Test User',
145
+ email: 'test@example.com',
146
+ picture: 'https://example.com/avatar.jpg'
147
+ });
148
+ }),
149
+ ];
150
+ ```
151
+
152
+ ### NotebookLM API 응답 모킹
153
+
154
+ ```typescript
155
+ // tests/mocks/notebooklm-handlers.ts
156
+ import { http, HttpResponse } from 'msw';
157
+
158
+ export const notebookLMHandlers = [
159
+ // 채팅 응답 모킹
160
+ http.post('https://notebooklm.google.com/api/chat', async ({ request }) => {
161
+ const body = await request.json();
162
+
163
+ return HttpResponse.json({
164
+ response: `Mocked response for: ${body.question}`,
165
+ sources: [
166
+ { title: 'Mock Source 1', snippet: 'Relevant content...' }
167
+ ],
168
+ thinking_time: 1500,
169
+ });
170
+ }),
171
+
172
+ // 노트북 목록 모킹
173
+ http.get('https://notebooklm.google.com/api/notebooks', () => {
174
+ return HttpResponse.json({
175
+ notebooks: [
176
+ { id: 'mock-001', title: 'Test Notebook 1' },
177
+ { id: 'mock-002', title: 'Test Notebook 2' },
178
+ ]
179
+ });
180
+ }),
181
+ ];
182
+ ```
183
+
184
+ ### Playwright MSW 통합 예제
185
+
186
+ ```typescript
187
+ // tests/e2e/authenticated-flow.spec.ts
188
+ import { test, expect } from '@playwright/test';
189
+ import { setupMSW } from '../mocks/setup';
190
+
191
+ test.describe('NotebookLM E2E Tests (Mocked)', () => {
192
+ test.beforeEach(async ({ page }) => {
193
+ // MSW 설정
194
+ await setupMSW(page, [
195
+ ...googleOAuthHandlers,
196
+ ...notebookLMHandlers,
197
+ ]);
198
+ });
199
+
200
+ test('should display notebooks after login', async ({ page }) => {
201
+ await page.goto('/');
202
+
203
+ // 로그인 버튼 클릭 (모킹된 OAuth 흐름)
204
+ await page.click('[data-testid="login-button"]');
205
+
206
+ // 노트북 목록 확인
207
+ await expect(page.getByText('Test Notebook 1')).toBeVisible();
208
+ await expect(page.getByText('Test Notebook 2')).toBeVisible();
209
+ });
210
+
211
+ test('should receive chat response', async ({ page }) => {
212
+ await page.goto('/notebook/mock-001');
213
+
214
+ await page.fill('[data-testid="chat-input"]', 'What is AI?');
215
+ await page.click('[data-testid="send-button"]');
216
+
217
+ // 모킹된 응답 확인
218
+ await expect(page.getByText('Mocked response for: What is AI?')).toBeVisible();
219
+ });
220
+ });
221
+ ```
222
+
223
+ ### 장단점
224
+
225
+ | ✅ 장점 | ❌ 단점 |
226
+ |:---|:---|
227
+ | 계정 위험 0% | 실제 서버 동작과 차이 가능 |
228
+ | 빠른 테스트 실행 | 모킹 유지보수 필요 |
229
+ | CI/CD 친화적 | API 변경 시 업데이트 필요 |
230
+ | 네트워크 독립적 | 모킹 로직 구현 비용 |
231
+
232
+ ---
233
+
234
+ ## 전략 2: 사전인증 세션 재사용
235
+
236
+ > [!IMPORTANT]
237
+ > **1회 수동 로그인 후 세션을 저장**하여 반복 로그인을 회피합니다.
238
+
239
+ ### 인증 상태 저장 및 재사용
240
+
241
+ ```mermaid
242
+ sequenceDiagram
243
+ participant Manual as 수동 로그인<br/>(1회)
244
+ participant Save as storageState<br/>저장
245
+ participant Test1 as Test 1
246
+ participant Test2 as Test 2
247
+ participant TestN as Test N
248
+
249
+ Manual->>Save: 쿠키/세션 저장
250
+ Save-->>Test1: 상태 로드
251
+ Save-->>Test2: 상태 로드
252
+ Save-->>TestN: 상태 로드
253
+ Note over Test1,TestN: 로그인 없이<br/>인증된 상태로 시작
254
+ ```
255
+
256
+ ### 구현: 세션 저장
257
+
258
+ ```typescript
259
+ // scripts/save-auth-state.ts
260
+ import { chromium } from 'patchright';
261
+ import path from 'path';
262
+
263
+ async function saveAuthenticationState() {
264
+ const browser = await chromium.launch({ headless: false });
265
+ const context = await browser.newContext();
266
+ const page = await context.newPage();
267
+
268
+ // 1. NotebookLM 접속
269
+ await page.goto('https://notebooklm.google.com');
270
+
271
+ // 2. 사용자에게 수동 로그인 요청
272
+ console.log('🔐 Please login manually in the browser window...');
273
+ console.log('⏳ Waiting for you to complete authentication...');
274
+
275
+ // 3. 인증 완료 대기 (특정 URL 또는 요소 감지)
276
+ await page.waitForURL('https://notebooklm.google.com/*', {
277
+ timeout: 300000 // 5분 대기
278
+ });
279
+
280
+ // 4. 인증 상태 저장
281
+ const authStatePath = path.join(__dirname, '../.auth/user-state.json');
282
+ await context.storageState({ path: authStatePath });
283
+
284
+ console.log(`✅ Authentication state saved to: ${authStatePath}`);
285
+ console.log('⚠️ Add this file to .gitignore!');
286
+
287
+ await browser.close();
288
+ }
289
+
290
+ saveAuthenticationState();
291
+ ```
292
+
293
+ ### 구현: 세션 로드
294
+
295
+ ```typescript
296
+ // playwright.config.ts
297
+ import { defineConfig } from '@playwright/test';
298
+
299
+ export default defineConfig({
300
+ projects: [
301
+ // 인증이 필요한 테스트
302
+ {
303
+ name: 'authenticated',
304
+ use: {
305
+ storageState: '.auth/user-state.json',
306
+ },
307
+ },
308
+ // 비인증 테스트
309
+ {
310
+ name: 'unauthenticated',
311
+ use: {
312
+ storageState: undefined,
313
+ },
314
+ },
315
+ ],
316
+ });
317
+ ```
318
+
319
+ ```typescript
320
+ // tests/e2e/notebook-operations.spec.ts
321
+ import { test, expect } from '@playwright/test';
322
+
323
+ test.use({ storageState: '.auth/user-state.json' });
324
+
325
+ test.describe('NotebookLM Authenticated Tests', () => {
326
+ test('should access notebooks without login', async ({ page }) => {
327
+ // 이미 인증된 상태로 시작
328
+ await page.goto('https://notebooklm.google.com');
329
+
330
+ // 즉시 노트북 목록 접근 가능
331
+ await expect(page.locator('[data-testid="notebooks-list"]')).toBeVisible();
332
+ });
333
+ });
334
+ ```
335
+
336
+ ### 세션 만료 처리
337
+
338
+ ```typescript
339
+ // utils/auth-manager.ts
340
+ import fs from 'fs/promises';
341
+
342
+ export async function isSessionValid(statePath: string): Promise<boolean> {
343
+ try {
344
+ const stateData = await fs.readFile(statePath, 'utf-8');
345
+ const state = JSON.parse(stateData);
346
+
347
+ // 쿠키 만료 확인
348
+ const now = Date.now() / 1000;
349
+ const criticalCookies = ['__Secure-1PSID', '__Secure-3PSID', 'SID'];
350
+
351
+ for (const cookie of state.cookies) {
352
+ if (criticalCookies.includes(cookie.name)) {
353
+ if (cookie.expires && cookie.expires < now) {
354
+ console.warn(`Cookie ${cookie.name} has expired`);
355
+ return false;
356
+ }
357
+ }
358
+ }
359
+
360
+ return true;
361
+ } catch (error) {
362
+ console.error('Failed to validate session:', error);
363
+ return false;
364
+ }
365
+ }
366
+ ```
367
+
368
+ ### 보안 주의사항
369
+
370
+ > [!CAUTION]
371
+ > 세션 파일은 민감한 정보를 포함합니다!
372
+
373
+ ```gitignore
374
+ # .gitignore
375
+ .auth/
376
+ *.auth.json
377
+ user-state.json
378
+ ```
379
+
380
+ ```yaml
381
+ # CI/CD 환경 변수로 관리
382
+ # GitHub Actions 예제
383
+ env:
384
+ AUTH_STATE: ${{ secrets.NOTEBOOK_AUTH_STATE }}
385
+ ```
386
+
387
+ ---
388
+
389
+ ## 전략 3: Patchright 스텔스 모드
390
+
391
+ ### Patchright란?
392
+
393
+ Patchright는 Playwright의 "언디텍티드" 버전으로, 브라우저 자동화 감지를 회피하도록 설계되었습니다.
394
+
395
+ > [!NOTE]
396
+ > 현재 프로젝트는 이미 Patchright 1.56.0을 사용하고 있습니다.
397
+
398
+ ### Patchright의 감지 회피 기법
399
+
400
+ ```mermaid
401
+ graph TD
402
+ subgraph "Playwright (감지됨)"
403
+ A1[Runtime.enable 호출] --> B1[navigator.webdriver = true]
404
+ B1 --> C1[HeadlessChrome UA]
405
+ C1 --> D1[--enable-automation 플래그]
406
+ end
407
+
408
+ subgraph "Patchright (스텔스)"
409
+ A2[Isolated Context 실행] --> B2[navigator.webdriver = undefined]
410
+ B2 --> C2[Chrome UA]
411
+ C2 --> D2[자동화 플래그 제거]
412
+ end
413
+
414
+ style A1 fill:#ff6b6b
415
+ style B1 fill:#ff6b6b
416
+ style C1 fill:#ff6b6b
417
+ style D1 fill:#ff6b6b
418
+ style A2 fill:#51cf66
419
+ style B2 fill:#51cf66
420
+ style C2 fill:#51cf66
421
+ style D2 fill:#51cf66
422
+ ```
423
+
424
+ ### 주요 패치 내용
425
+
426
+ | 감지 포인트 | Playwright | Patchright |
427
+ |:---|:---|:---|
428
+ | `navigator.webdriver` | `true` | `undefined` |
429
+ | User-Agent | `HeadlessChrome` | `Chrome` |
430
+ | `Runtime.enable` | 호출됨 | 격리된 컨텍스트 사용 |
431
+ | `Console.enable` | 호출됨 | 패치됨 |
432
+ | `--enable-automation` | 있음 | 제거됨 |
433
+ | 팝업 차단 | 비활성화 | 기본값 유지 |
434
+
435
+ ### 현재 프로젝트의 스텔스 구현
436
+
437
+ ```typescript
438
+ // src/utils/stealth-utils.ts (현재 구현)
439
+ export async function humanType(
440
+ page: Page,
441
+ selector: string,
442
+ text: string,
443
+ options?: { wpm?: number; typos?: boolean }
444
+ ): Promise<void> {
445
+ if (!CONFIG.stealthEnabled || !CONFIG.stealthHumanTyping) {
446
+ await page.fill(selector, text);
447
+ return;
448
+ }
449
+
450
+ // 인간적인 타이핑 시뮬레이션
451
+ const wpm = options?.wpm ?? randomInt(CONFIG.typingMinWPM, CONFIG.typingMaxWPM);
452
+ const baseDelay = (60 * 1000) / (wpm * 5);
453
+
454
+ for (let i = 0; i < text.length; i++) {
455
+ const char = text[i];
456
+
457
+ // 오타 시뮬레이션 (5% 확률)
458
+ if (options?.typos && Math.random() < 0.05 && i > 0) {
459
+ const typoChar = randomChar();
460
+ await page.fill(selector, currentText + typoChar);
461
+ await randomDelay(100, 200);
462
+ // 백스페이스
463
+ currentText = currentText.slice(0, -1);
464
+ await page.fill(selector, currentText);
465
+ }
466
+
467
+ currentText += char;
468
+ await page.fill(selector, currentText);
469
+
470
+ // 문자별 지연
471
+ let delay = baseDelay * randomFloat(0.8, 1.2);
472
+ if (char === ' ' || char === '.' || char === '!') {
473
+ delay *= 1.5; // 문장부호 후 더 긴 지연
474
+ }
475
+ await sleep(delay);
476
+ }
477
+ }
478
+ ```
479
+
480
+ ```typescript
481
+ // src/utils/stealth-utils.ts
482
+ export async function randomMouseMovement(
483
+ page: Page,
484
+ endX?: number,
485
+ endY?: number,
486
+ steps?: number
487
+ ): Promise<void> {
488
+ if (!CONFIG.stealthEnabled || !CONFIG.stealthMouseMovements) {
489
+ return;
490
+ }
491
+
492
+ // 베지어 커브 기반 마우스 이동
493
+ for (let step = 0; step < steps; step++) {
494
+ const progress = step / steps;
495
+
496
+ // 자연스러운 곡선 이동
497
+ const jitterX = Math.sin(progress * Math.PI) * randomInt(-10, 10);
498
+ const jitterY = Math.cos(progress * Math.PI) * randomInt(-10, 10);
499
+
500
+ const currentX = startX + (endX - startX) * progress + jitterX;
501
+ const currentY = startY + (endY - startY) * progress + jitterY;
502
+
503
+ await page.mouse.move(currentX, currentY);
504
+
505
+ // 이동 속도 변화 (가속→감속)
506
+ const delay = 10 + 30 * Math.sin(2.5 * progress);
507
+ await sleep(delay);
508
+ }
509
+ }
510
+ ```
511
+
512
+ ### 고급 스텔스 설정
513
+
514
+ ```typescript
515
+ // patchright-config.ts
516
+ import { chromium } from 'patchright';
517
+
518
+ export async function launchStealthBrowser() {
519
+ const browser = await chromium.launch({
520
+ headless: false, // 헤드리스 감지 회피
521
+ channel: 'chrome', // 실제 Chrome 사용
522
+
523
+ args: [
524
+ '--disable-blink-features=AutomationControlled',
525
+ '--no-sandbox',
526
+ '--disable-setuid-sandbox',
527
+ '--disable-dev-shm-usage',
528
+ '--disable-accelerated-2d-canvas',
529
+ '--no-first-run',
530
+ '--no-zygote',
531
+ '--disable-gpu',
532
+ '--hide-scrollbars',
533
+ '--mute-audio',
534
+ ],
535
+
536
+ ignoreDefaultArgs: ['--enable-automation'],
537
+ });
538
+
539
+ const context = await browser.newContext({
540
+ // 실제 사용자 User-Agent
541
+ userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) ' +
542
+ 'AppleWebKit/537.36 (KHTML, like Gecko) ' +
543
+ 'Chrome/120.0.0.0 Safari/537.36',
544
+
545
+ // 실제 뷰포트 크기
546
+ viewport: { width: 1920, height: 1080 },
547
+
548
+ // 지역 설정
549
+ locale: 'ko-KR',
550
+ timezoneId: 'Asia/Seoul',
551
+
552
+ // 권한 설정
553
+ permissions: ['geolocation', 'notifications'],
554
+
555
+ // 지리적 위치
556
+ geolocation: { latitude: 37.5665, longitude: 126.9780 }, // 서울
557
+ });
558
+
559
+ return { browser, context };
560
+ }
561
+ ```
562
+
563
+ ### 여전히 존재하는 위험
564
+
565
+ > [!WARNING]
566
+ > Patchright를 사용해도 **Google 로그인 자동화는 위험**합니다!
567
+
568
+ - Google은 지속적으로 감지 로직을 업데이트
569
+ - 오픈소스 스텔스 도구는 뒤처질 수 있음
570
+ - 계정 정지는 복구가 어려움
571
+
572
+ ---
573
+
574
+ ## 전략 4: Google API 대안 탐색
575
+
576
+ ### NotebookLM Enterprise API
577
+
578
+ Google은 기업 사용자를 위한 NotebookLM Enterprise API를 제공합니다.
579
+
580
+ ```http
581
+ # NotebookLM Enterprise API Endpoints
582
+ POST /v1/notebooks
583
+ GET /v1/notebooks/{notebookId}
584
+ POST /v1/notebooks/{notebookId}/sources
585
+ POST /v1/notebooks/{notebookId}/chat
586
+ ```
587
+
588
+ > [!NOTE]
589
+ > Enterprise API 접근은 Google Cloud 계정과 별도 계약이 필요합니다.
590
+
591
+ ### 오픈소스 대안: Open Notebook
592
+
593
+ [Open Notebook](https://github.com/smol-ai/open-notebook)은 NotebookLM의 오픈소스 대안으로, 자체 호스팅이 가능합니다.
594
+
595
+ ```bash
596
+ # Docker로 Open Notebook 실행
597
+ docker run -d \
598
+ -p 3000:3000 \
599
+ -e LLM_PROVIDER=openai \
600
+ -e OPENAI_API_KEY=sk-xxx \
601
+ opennotebook/server:latest
602
+ ```
603
+
604
+ ### Vertex AI 대안
605
+
606
+ Google Cloud Vertex AI를 사용하면 공식 API로 유사한 기능을 구현할 수 있습니다.
607
+
608
+ ```typescript
609
+ // Vertex AI 문서 요약 예제
610
+ import { VertexAI } from '@google-cloud/vertexai';
611
+
612
+ const vertexAI = new VertexAI({
613
+ project: 'your-project',
614
+ location: 'us-central1',
615
+ });
616
+
617
+ const model = vertexAI.getGenerativeModel({
618
+ model: 'gemini-1.5-pro',
619
+ });
620
+
621
+ async function summarizeDocument(content: string) {
622
+ const result = await model.generateContent({
623
+ contents: [{ role: 'user', parts: [{ text: content }] }],
624
+ systemInstruction: 'You are a document summarization assistant.',
625
+ });
626
+
627
+ return result.response.candidates[0].content;
628
+ }
629
+ ```
630
+
631
+ ---
632
+
633
+ ## 권장 테스트 아키텍처
634
+
635
+ ### 테스트 피라미드 적용
636
+
637
+ ```mermaid
638
+ graph TB
639
+ subgraph "E2E Tests (실제 브라우저)"
640
+ E1[세션 재사용 테스트]
641
+ E2[Patchright 스텔스]
642
+ end
643
+
644
+ subgraph "Integration Tests (가장 권장)"
645
+ I1[MSW 모킹 테스트]
646
+ I2[API 통합 테스트]
647
+ end
648
+
649
+ subgraph "Unit Tests"
650
+ U1[유틸리티 테스트]
651
+ U2[컴포넌트 테스트]
652
+ end
653
+
654
+ E1 --> I1
655
+ E2 --> I1
656
+ I1 --> U1
657
+ I2 --> U2
658
+
659
+ style I1 fill:#40c057
660
+ style I2 fill:#40c057
661
+ ```
662
+
663
+ ### 실제 테스트 전략 구성
664
+
665
+ ```typescript
666
+ // vitest.config.ts
667
+ import { defineConfig } from 'vitest/config';
668
+
669
+ export default defineConfig({
670
+ test: {
671
+ // 단위 테스트: 빠르고 안전
672
+ include: ['src/**/*.test.ts'],
673
+ exclude: ['tests/e2e/**'],
674
+
675
+ // 커버리지
676
+ coverage: {
677
+ reporter: ['text', 'json', 'html'],
678
+ },
679
+ },
680
+ });
681
+ ```
682
+
683
+ ```typescript
684
+ // playwright.config.ts
685
+ import { defineConfig } from '@playwright/test';
686
+
687
+ export default defineConfig({
688
+ projects: [
689
+ // 1. 모킹 기반 테스트 (권장)
690
+ {
691
+ name: 'mocked',
692
+ testDir: './tests/mocked',
693
+ use: {
694
+ baseURL: 'http://localhost:3000',
695
+ },
696
+ },
697
+
698
+ // 2. 세션 재사용 테스트 (수동 로그인 필요)
699
+ {
700
+ name: 'authenticated',
701
+ testDir: './tests/authenticated',
702
+ use: {
703
+ storageState: '.auth/user-state.json',
704
+ baseURL: 'https://notebooklm.google.com',
705
+ },
706
+ },
707
+ ],
708
+
709
+ // 병렬 실행 제한 (실제 서버 테스트)
710
+ workers: process.env.CI ? 1 : undefined,
711
+
712
+ // 재시도 설정
713
+ retries: process.env.CI ? 2 : 0,
714
+ });
715
+ ```
716
+
717
+ ---
718
+
719
+ ## 결론 및 권장사항
720
+
721
+ ### 최종 권장 전략
722
+
723
+ 1. **개발 단계**: MSW 모킹 사용 (100% 안전)
724
+ 2. **로컬 검증**: 세션 재사용 + Patchright 스텔스
725
+ 3. **CI/CD**: 모킹 테스트만 실행
726
+ 4. **프로덕션 검증**: 수동 테스트 또는 Enterprise API
727
+
728
+ ### 체크리스트
729
+
730
+ - [ ] MSW 핸들러 구현
731
+ - [ ] Playwright storageState 설정
732
+ - [ ] .gitignore에 인증 파일 추가
733
+ - [ ] CI/CD 파이프라인에 모킹 테스트 통합
734
+ - [ ] 세션 만료 처리 로직 구현
735
+ - [ ] 테스트 전용 Google 계정 생성 (선택)
736
+
737
+ ### 절대 하지 말아야 할 것
738
+
739
+ > [!CAUTION]
740
+ >
741
+ > 1. ❌ 실제 Google 계정으로 자동화 로그인 반복 시도
742
+ > 2. ❌ 인증 파일을 버전 관리에 포함
743
+ > 3. ❌ CI/CD에서 실제 Google 로그인 실행
744
+ > 4. ❌ 프록시 우회로 다중 세션 생성 시도
745
+
746
+ ---
747
+
748
+ ## 참고 자료
749
+
750
+ - [Patchright GitHub](https://github.com/Kaliiiiiiiiii-Vinyzu/patchright-nodejs)
751
+ - [MSW Documentation](https://mswjs.io/)
752
+ - [Playwright Authentication](https://playwright.dev/docs/auth)
753
+ - [Google Cloud Vertex AI](https://cloud.google.com/vertex-ai)
754
+ - [Open Notebook (오픈소스 대안)](https://github.com/smol-ai/open-notebook)